This is the 24th day of my participation in the First Challenge 2022.
Previously on & Background
ProcessKey, processRef, checkInFor, processSlotContent:
-
ProcessKey: Checks the key attribute on the element and the restrictions on using the key;
-
ProcessRef: Handles the ref property, if ref is wrapped by V-for, ref points to an array;
-
CheckInFor: Checks if the parent element of the current element has a V-for directive;
-
ProcessSlotContent: processSlotContent, get slot name, dynamic slot, and v-slot instructions after 2.6.x;
Second, the processSlotOutlet
Methods location: SRC/compiler/parser/index. The js – > function processSlotOutlet
Method parameters: EL, AST node object
The slot () method parses the slot tags of the closed and gets the name of the named slot and sets it to the EL property.
function processSlotOutlet (el) {
if (el.tag === 'slot') {
el.slotName = getBindingAttr(el, 'name')
// Do not use keys on slot tags
if(process.env.NODE_ENV ! = ='production' && el.key) {
warn(
` `key` does not work on <slot> because slots are abstract outlets ` +
`and can possibly expand into multiple elements. ` +
`Use the key on a wrapping element instead.`,
getRawBindingAttr(el, 'key')}}}Copy the code
Third, processComponent
Methods location: SRC/compiler/parser/index. The js – > function processComponent
Method parameters: EL, AST node object
The El.inlineTemplate = true () method parses dynamic components and sets the el.component.ponent = is property binding. If the Component tag contains an inline-template property, set el.inlineTemplate = true
Take the following code for example:
<component :is="compName" inline-template>
<div>some content,not slot other than compName's template</div>
</component>
Copy the code
Function processComponent (el) {let binding; el.component = compName if ((binding = getBindingAttr(el, 'is'))) {el.component.ponent = binding} If (getAndRemoveAttr(el, 'inline-template')! If (getAndRemoveAttr(el, 'inline-template')! = null) { el.inlineTemplate = true } }Copy the code
Four, transforms
Transforms Is not a method, but an array of methods, derived via pluckModuleFunction(options.modules, ‘transformNode’), Just like the preTransformNode, postTransform methods;
Modules come from createCompiler(baseOpitons). Modules in baseOptions have klass,style, and Model items. PluckModuleFunctions pick out the specified methods from these three to compose an array;
The transformNode methods only klass and style are exported, but model is not exported. So there are two methods in the Transforms array that handle the AST’s class name and style style, respectively;
Vue bind:class, Vue bind:class, Vue bind:class, Vue bind:class, Vue bind:class Add el. StaticClass, el. ClassBinding, El. StaticStyle and el. StyleBinding.
4.1 transformNode < – modules/style.css. Js
Methods location: SRC/platforms/web/compiler/modules/style.css. Js – > functions provides transformNode
Method parameters:
el
.ast
The node objectOptions: createCompiler (baseOptions)
Method receives
Unbind the v-bin:style / :style attributes from el and assign them to el. StaticStyle and el. StyleBinding ‘respectively
function transformNode (el: ASTElement, options: CompilerOptions) {
const warn = options.warn || baseWarn
// Get
coloer: red
const staticStyle = getAndRemoveAttr(el, 'style')
if (staticStyle) {
if(process.env.NODE_ENV ! = ='production') {
const res = parseText(staticStyle, options.delimiters)
// if you parse to delimiter from xx, it is a dynamic style
// If
, use :style="val"
if (res) {
warn(
`style="${staticStyle}": ` +
'Interpolation inside attributes has been removed. ' +
'Use v-bind or the colon shorthand instead. For example, ' +
'instead of <div style="{{ val }}">, use <div :style="val">.',
el.rawAttrsMap['style'])}}// assign a staticStyle to el. StaticStyle
el.staticStyle = JSON.stringify(parseStyleText(staticStyle))
}
// Assign to el.styleBinding
const styleBinding = getBindingAttr(el, 'style'.false /* getStatic */)
if (styleBinding) {
el.styleBinding = styleBinding
}
}
Copy the code
4.2 tranfromNode < – modules/class. Js
function transformNode (el: ASTElement, options: CompilerOptions) {
/ / log
const warn = options.warn || baseWarn
// <div class="someStaticClassName"></div>
// Get the class attribute value on the element someStaticClassName
const staticClass = getAndRemoveAttr(el, 'class')
if(process.env.NODE_ENV ! = ='production' && staticClass) {
const res = parseText(staticClass, options.delimiters)
//
if (res) {
warn(
`class="${staticClass}": ` +
'Interpolation inside attributes has been removed. ' +
'Use v-bind or the colon shorthand instead. For example, ' +
'instead of <div class="{{ val }}">, use <div :class="val">.',
el.rawAttrsMap['class'])}}// assign the staticClass attribute to el.staticClass
if (staticClass) {
el.staticClass = JSON.stringify(staticClass.replace(/\s+/g.' ').trim())
}
// Get the dynamically bound class property value and copy it to el.classBinding
const classBinding = getBindingAttr(el, 'class'.false /* getStatic */)
if (classBinding) {
el.classBinding = classBinding
}
}
Copy the code
Fifth, processAttrs
Methods location: SRC/compiler/parser/index. The js – > function processAttrs
Method parameters: EL, AST node object
The attrsList () method iterates over the attributes on the object attrsList to obtain the name and value of each item.
- Determine if the attribute is a directive if it is:
- 1.1 set up
el.hasBinding = true
; - 1.2 Save modifiers on parse instructions to
modifiers
Variable, and then replace the modifier part on the property with an empty string; - 1.3 Checking attributes with re
name
Whether to useV - bind or:
Dynamically bound values to attributesname
Dynamic binding ofV - bind or:
Replace with empty, leaving only the bound property itself; Then parsingvalue
Filter on; - 1.4 Then use the re to match whether there are dynamic parameters on the instruction, and the result assigns a value
isDynamic
, the so-called dynamic parameter, for example<a v-bind:[attributeName]="url"> ... </a>
In square bracketsattrbuteName
Is the dynamic parameter, when the dynamic parameter is obtained, the parameter name in square brackets is obtained, i.ename.slice(1, -1)
- 1.5 If modifier
modifiers
Not null, if there are no dynamic parametersProp, camel
The modifier; To deal withsync
The modifier,sync
The modifier is aUpdate: the property name
Syntactic sugar for the event name that is executed when the event is triggeredsyncGen
Code, which is responsible for updating dataAssignment or $set
) - 1.6 if it is
v-on
Instruction that handles dynamic parameter names and gets withoutv-on
or@
Gets the part without square brackets if it is a dynamically bound parameter, for example<div v-on:[someDynamicEventName]></div>
someDynamicEventName
And then calladdHandler
Rebind events; - 1.7 The last is the common instruction, which obtains the common instruction name and parameters, determines whether there are dynamic parameters, and then invokes
addDirective
Add instruction:el.directives.push()
- 1.1 set up
- If it is not an instruction specification or a normal literal property, then call
addAttr()
把name
和value
Set it toel.dynamicAttrs
orel.attrs
Here you can see it very clearlyattrs
Is a static property, willel.plain
Set tofalse
; If not component andname
是'muted'
And must useprop
Called when a property is boundaddProp
The bindingmuted
Properties;
function processAttrs (el) {
const list = el.attrsList
let i, l, name, rawName, value, modifiers, syncGen, isDynamic
for (i = 0, l = list.length; i < l; i++) {
name = rawName = list[i].name
value = list[i].value
if (dirRE.test(name)) {
// If it is a command
// mark element as dynamic
el.hasBindings = true
/ / modifier
modifiers = parseModifiers(name.replace(dirRE, ' '))
// Syntax modifiers that support the.foo abbreviation
if (process.env.VBIND_PROP_SHORTHAND && propBindRE.test(name)) {
(modifiers || (modifiers = {})).prop = true
name = `. ` + name.slice(1).replace(modifierRE, ' ')}else if (modifiers) {
// The name does not contain modifiers
name = name.replace(modifierRE, ' ')}if (bindRE.test(name)) { // v-bind
name = name.replace(bindRE, ' ')
value = parseFilters(value)
isDynamic = dynamicArgRE.test(name) // Whether it is a dynamic parameter
if (isDynamic) {
name = name.slice(1, -1)}if( process.env.NODE_ENV ! = ='production' &&
value.trim().length === 0
) {
warn(
`The value for a v-bind expression cannot be empty. Found in "v-bind:${name}"`)}if (modifiers) {
if(modifiers.prop && ! isDynamic) { name = camelize(name)if (name === 'innerHtml') name = 'innerHTML'
}
if(modifiers.camel && ! isDynamic) { name = camelize(name) }if (modifiers.sync) {
// Handle the sync modifier, which is how the sync modifier works
syncGen = genAssignmentCode(value, `$event`)
if(! isDynamic) { addHandler( el,`update:${camelize(name)}`,
syncGen,
null.false,
warn,
list[i]
)
if(hyphenate(name) ! == camelize(name)) { addHandler( el,`update:${hyphenate(name)}`,
syncGen,
null.false,
warn,
list[i]
)
}
} else {
// handler w/ dynamic event name
addHandler(
el,
`"update:"+(${name}) `,
syncGen,
null.false,
warn,
list[i],
true // dynamic)}}}if((modifiers && modifiers.prop) || ( ! el.component && platformMustUseProp(el.tag, el.attrsMap.type, name) )) { addProp(el, name, value, list[i], isDynamic) }else {
addAttr(el, name, value, list[i], isDynamic)
}
} else if (onRE.test(name)) { // v-on
name = name.replace(onRE, ' ')
isDynamic = dynamicArgRE.test(name)
if (isDynamic) {
name = name.slice(1, -1)
}
addHandler(el, name, value, modifiers, false, warn, list[i], isDynamic)
} else { // normal directives
name = name.replace(dirRE, ' ')
// parse arg
const argMatch = name.match(argRE)
let arg = argMatch && argMatch[1]
isDynamic = false
if (arg) {
name = name.slice(0, -(arg.length + 1))
if (dynamicArgRE.test(arg)) {
arg = arg.slice(1, -1)
isDynamic = true
}
}
addDirective(el, name, rawName, value, arg, isDynamic, modifiers, list[i])
if(process.env.NODE_ENV ! = ='production' && name === 'model') {
checkForAliasModel(el, value)
}
}
} else {
// literal attribute
if(process.env.NODE_ENV ! = ='production') {
const res = parseText(value, delimiters)
if (res) {
warn(
`${name}="${value}": ` +
'Interpolation inside attributes has been removed. ' +
'Use v-bind or the colon shorthand instead. For example, ' +
'instead of <div id="{{ val }}">, use <div :id="val">.',
list[i]
)
}
}
addAttr(el, name, JSON.stringify(value), list[i])
// #6887 firefox doesn't update muted state if set via attribute
// even immediately after element creation
if(! el.component && name ==='muted' &&
platformMustUseProp(el.tag, el.attrsMap.type, name)) {
addProp(el, name, 'true', list[i])
}
}
}
}
Copy the code
Six, summarized
This essay continues the discussion of closeElement’s tool methods, as follows:
-
ProcessSlotOutlet: Resolves the slot labels of the closed and gets the slot name set to the EL;
-
ProcessComponent: Resolves dynamic components, resolves IS properties and inline-template
-
Transforms: transoformNode methods exported by the class/style module process static and dynamic class names or styles
-
ProcessAttrs: Handles attrs on elements, parameters of instructions, event bindings, modifiers, and so on;
This is the end of closeElment, and all the logic of options.start has been completed