preface
The first part of the compiler, how to compile HTML template strings into aN AST, was explained in detail. Today we bring the second part of the compiler, optimizing the AST, also known as static markup.
The target
Learn more about the compiler’s static markup process
The source code interpretation
The entrance
/src/compiler/index.js
/** * All this work has been done with the sole purpose of building platform-specific compilation options. For example, web platform * * 1, parse HTML template into AST * 2, static mark ast tree * 3, ast generation rendering function * static rendering function into code. StaticRenderFns array * code Execute the render function to get vNode */ for the dynamic render function *
export const createCompiler = createCompilerCreator(function baseCompile (template: string, options: CompilerOptions) :CompiledResult {
// Parse the template into an AST. Each AST object has all the information of the element, such as label information, attribute information, slot information, parent node, child node, etc.
// For those attributes, see the start and end methods that handle the start and end tags
const ast = parse(template.trim(), options)
// Optimize by traversing the AST, marking each node statically
// Mark whether each node is static or not, and then further mark the static root node
// The static nodes can be skipped during subsequent updates
// Mark the static root, which is used in the render function generation stage, to generate the render function of the static root node
if(options.optimize ! = =false) {
optimize(ast, options)
}
// Generate a rendering function from the AST to generate code like this: code.render = "_c('div',{attrs:{"id":"app"}},_l((arr),function(item){return _c('div',{key:item},[_v(_s(item))])}),0)"
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
Copy the code
optimize
/src/compiler/optimizer.js
/** * optimizes: * Traverses the AST, marking whether each node is static or dynamic, and then marking the static root node * so that you don't need to pay attention to these nodes during subsequent updates */
export function optimize(root: ? ASTElement, options: CompilerOptions) {
if(! root)return
/** * options.staticKeys = 'staticClass,staticStyle' * isStaticKey = function(val) { return map[val] } */
isStaticKey = genStaticKeysCached(options.staticKeys || ' ')
// The platform retains the label
isPlatformReservedTag = options.isReservedTag || no
// Go through all the nodes and set the static property to each node, indicating whether it is static or not
markStatic(root)
// For a node to become a static root, the following conditions are required:
// If the node itself is static and has children, and the child is not just a text node, it is marked as static root
// The static root node should not only have children of static text, because that would be too low. In this case, it should always be updated
markStaticRoots(root, false)}Copy the code
markStatic
/src/compiler/optimizer.js
/** * set the static property on all nodes to indicate whether they are static nodes * Note: If any of the children are dynamic nodes, then the parent node is also considered dynamic *@param {*} node
* @returns * /
function markStatic(node: ASTNode) {
// Use node.static to indicate whether a node is static
node.static = isStatic(node)
if (node.type === 1) {
/** * Do not set the component's slot content to a static node, this can avoid: * 1, the component cannot change the slot node * 2, the static slot content fails during hot overload */
if(! isPlatformReservedTag(node.tag) && node.tag ! = ='slot' &&
node.attrsMap['inline-template'] = =null
) {
// Recursively terminates if the node is not a platform reserved tag && nor a slot tag && nor an inline template
return
}
// Iterate over the children, calling markStatic recursively to mark the static properties of the children
for (let i = 0, l = node.children.length; i < l; i++) {
const child = node.children[i]
markStatic(child)
// If the child node is non-static, update the parent node to be non-static
if(! child.static) { node.static =false}}// If a node has the v-if, v-else-if, v-else-if, v-else-if instructions, then mark the static value of the node in the block
if (node.ifConditions) {
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
const block = node.ifConditions[i].block
markStatic(block)
if(! block.static) { node.static =false
}
}
}
}
}
Copy the code
isStatic
/src/compiler/optimizer.js
/** * determine whether a node is static: * determine whether a node is static by using a custom node.type, 2: expression => dynamic, 3: Text => Static * All the v-bind, V-if, V-for directives are dynamic nodes * the component is dynamic node * the parent node is template tag containing v-for directive, then it is dynamic node *@param {*} node
* @returns boolean* /
function isStatic(node: ASTNode) :boolean {
if (node.type === 2) { // expression
// For example: {{MSG}}
return false
}
if (node.type === 3) { // text
return true
}
return!!!!! (node.pre || ( ! node.hasBindings &&// no dynamic bindings! node.if && ! node.for &&// not v-if or v-for or v-else! isBuiltInTag(node.tag) &&// not a built-in
isPlatformReservedTag(node.tag) && // not a component! isDirectChildOfTemplateFor(node) &&Object.keys(node).every(isStaticKey)
))
}
Copy the code
markStaticRoots
/src/compiler/optimizer.js
For a node to become a static root, the following conditions are required: * If the node itself is static and has children that are not just a text node, then it is marked as static root * The static root cannot have only children of static text, because that would be too low a benefit, in which case it would be good to always update it * *@param { ASTElement } Node Indicates the current node *@param { boolean } IsInFor Whether the current node is wrapped in the node where the V-for directive is located */
function markStaticRoots(node: ASTNode, isInFor: boolean) {
if (node.type === 1) {
if (node.static || node.once) {
Node.staticinfor = true or false if the node is static or has a V-once directive on it
node.staticInFor = isInFor
}
if(node.static && node.children.length && ! ( node.children.length ===1 &&
node.children[0].type === 3
)) {
// if the node itself is static and has children that are not just a text node, mark it as staticRoot => node.staticroot = true, otherwise non-static root
node.staticRoot = true
return
} else {
node.staticRoot = false
}
// If the current node is not a static root, recursively iterate through the children, marking the static root
if (node.children) {
for (let i = 0, l = node.children.length; i < l; i++) { markStaticRoots(node.children[i], isInFor || !! node.for) } }// Mark the static root for the block node if it has v-if, V-else-if, and V-else directives
if (node.ifConditions) {
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
markStaticRoots(node.ifConditions[i].block, isInFor)
}
}
}
}
Copy the code
conclusion
-
Interviewer: What does Vue’s compiler do briefly?
A:
Vue’s compiler does three things:
-
Parse the component’s HTML template into an AST object
-
Optimization, traversing the AST, making static tags for each node, marking whether it is static node or not, and then further marking the static root node, so that these static nodes can be skipped in the process of subsequent updates; The tag static root is used in the render function generation phase to generate the render function for the static root node
-
The staticRenderFns array contains all static node render functions
-
-
Interviewer: Tell me more about the static tagging process
A:
-
Marking static nodes
-
Mark all element nodes recursively
-
If the node itself is static, but there are non-static child nodes, modify the node to be non-static
-
-
Mark the static root node, based on the static node, further mark the static root node
-
If the node itself is static && and has children && whose children are not all text nodes, it is marked as static root
-
If the node itself is not a static root, it recursively traverses all the children, marking the static root among them
-
-
-
Interviewer: What kind of nodes can be marked as static nodes?
A:
-
Text node
-
No v-bind, V-for, or V-if instructions exist on the node
-
The component
-
Form a complete set of video
Vue source code interpretation (9) — compiler optimization
Please focus on
Welcome everyone to follow my gold mining account and B station, if the content has to help you, welcome everyone to like, collect + attention
link
-
Vue source code interpretation (1) – preface
-
Vue source code interpretation (2) — Vue initialization process
-
Vue source code interpretation (3) – response principle
-
Vue source code interpretation (4) — asynchronous update
-
Vue source code Interpretation (5) — Global API
-
Vue source code interpretation (6) — instance method
-
(7) — Hook Event
-
Vue source code interpretation (8) — compiler parsing
-
Vue source code interpretation (9) — compiler optimization
-
Vue source code interpretation (10) — compiler generation rendering function
-
(11) — Render helper
-
Vue source code interpretation (12) — patch
Learning exchange group
link