Template compilation
The foregoing
The original intention of this article is to let more students know and understand the compilation of VUE template, so this article mainly focuses on the stage process, not involving too much low-level code logic, please be patient to watch, if you want to know more about the underlying code, please click me
thinking
HTML is a tag language, only JS can realize judgment, loop, and template has instructions, interpolation, JS expressions, can realize judgment, loop, etc., so the template is not HTML, so the template must be converted to some JS code, this compilation is how to proceed?
parsing
Template compilation is the process of compiling a template into a render function, which can be roughly divided into three stages:
phase
Parse the parser
The parser basically converts the template string to the Element ASTs
Template string
<div>
<p>{{message}}</p>
</div>
Copy the code
element ASTs
An AST is an abstract syntax tree, similar to Vnode, which uses JavaScript objects to describe the tree representation of nodes
{tag: "div" // The type of the node (1 tag, 2 text nodes containing literal expressions, 3 plain text nodes or comment nodes) type: 1, // staticRoot: false, // static node static: False, plain: true, // The parent node element describes the object reference parent: undefined, // Only if the node type is 1, the attrsList attribute is available, which is an array of objects that stores the original HTML attribute name and the value attrsList: [], // children: [{tag: "p" type:], // children: [{tag: "p" type:], // Children: [{tag: "p" type:] 1, staticRoot: false, static: false, plain: true, parent: {tag: "div", ...}, attrsList: [], attrsMap: {}, children: [{type: 2, text: "{{message}}", static: false, // Expression: "_s(message)"}]}}Copy the code
Rules for interception
We determine whether to intercept tags or text by determining the value of html.indexof(‘<‘) in the template.
The interception process
String part
`<div><p>{{message}}<p></div>`
Copy the code
Intercept the part of the process
First intercept
- Determine the value of html.indexof(‘<‘) in the template as zero (comment, conditional comment, docType, start tag, end tag)
- Get the current tag name div, and then cut off the ‘<div’ portion of the match to get the new string
><p>{{message}}</p></div>
- If the match is successful, the attribute list of the tag will be obtained. If the match is unsuccessful, the attribute list of the tag will be an empty array
- After the attribute is truncated, the match is used to get information about whether it is a self-closing tag, and then the matched string is truncated to get a new string
<p>{{message}}</p></div>
- If it does not, a tree node of element type will be created. If it does, it will be set to a child of currentParent and the current node will be pushed onto the stack
<p>{{message}}<p></div> 'Copy the code
The second intercept
Above / * * * / / / after the match, for the rest of the string section: ` {{message}} < / p > < / div > `Copy the code
Third intercept
-
Determine the value of html.indexof(‘<‘) in the template, greater than or equal to zero (either text or expression)
-
Query the most recent ‘<‘ and match whether it matches (start tag, end tag, comment, or conditional comment). If the match is successful, the traversal ends. If the match is unsuccessful, the traversal continues
Such as:
A < b
=> Text part A < b, hit the end tag
a<b</p>
=> Texta
Hit the start tag <b
</p></div> '</p></divCopy the code
Intercept number four
- Determine the value of html.indexof(‘<‘) in the template as zero (comment, conditional comment, docType, start tag, end tag)
- The ended tag matches successfully, and then intercepts the matched re
</p>
Section to get the new string</div>
- Matching the end tag pops a node ‘p’ from the stack and sets the last node in the stack ‘div’ to currentParent
</div> </div> </div> </div> </divCopy the code
Intercept number five
/** * as above */ endCopy the code
Parser summary
-
The process of converting template strings to the Element ASTs is a process of constantly intercepting strings and parsing them.
-
When the start tag is matched, the corresponding start tag is intercepted, the basic structure of the AST is defined, and attributes (attrs, tagName), instructions, etc., are parsed on the tag, and the tag is pushed onto the stack
-
If you match the end tag, you need to match the tagName of each item in the stack from the back to the front, removing all items after the matched tag so that the last item in the stack is the parent element
-
In the parsing stage, the nodes will be leveled without hierarchical relationship. Through observation, we can find the node tree, and it can be found that the innermost node is parsed, and the last parsed element is usually the parent element. Therefore, we record the hierarchical relationship of nodes through a stack.
-
The self-closing tag has no child nodes, so it does not need to be pushed onto the stack.
Optimize the optimizer
The function of the optimizer is mainly to optimize the static content of the generated AST and mark the static nodes. In order to re-render each time, there is no need to create new nodes for the static subtree, and the patch process in the virtual DOM can be skipped (that is, there is no need to participate in the second page rendering, which greatly improves the rendering efficiency).
Static node
Walk through the AST syntax tree to find and label all static nodes
function isStatic (node) { // expression if (node.type === 2) { return false } // text if (node.type === 3) { return true } /** 1. You can't use dynamic binding syntax, that is, you can't have v-, @, : attributes on the tag; 2. Do not use the V-if, V-else, or V-for commands. 3. It cannot be a built-in component. That is, the label name cannot be slot or Component. 4. The label name must be a platform reserved label, that is, not a component. 5. The parent node of the current node cannot be the template tag with v-for. 6. All attributes of the nodes of the static node to some key, the key must be note: the key of static node is limited, it is only the type, the tag, attrsList, attrsMap, plain, the parent, the children, one of attrs; */ return !! (node.pre || ( ! node.hasBindings && ! node.if && ! node.for && ! isBuiltInTag(node.tag) && isPlatformReservedTag(node.tag) && ! isDirectChildOfTemplateFor(node) && Object.keys(node).every(isStaticKey) )) }Copy the code
Static root node
Walk through the tree after the previous steps to find the static root node and label it
Optimizer summary
-
Nodes that do not use vUE’s unique syntax (except v-pre v-once) can be called static nodes
-
Static node: The current node and all its children are static nodes
-
Static root node: a node whose parent node is a dynamic node and all its children are static nodes
Generate code generator
The purpose of the code generator is to generate code strings from the AST syntax tree. The code strings are wrapped in the render function. After executing the render function, a VNode can be obtained
JS with syntax
Use with, can change the way you find within {} free variables, will be free variables, {} as obj attribute to find, if can't find matching obj properties, would be an error const obj = {a: 100, b: 200} with(obj) {console.log(a) console.log(b) // console.log(c) // error}Copy the code
Code string
Parse the element ASTs generated by parse and concatenate them into a string
with(this){return _c('div',_c('p',[_v(message)])])}
Copy the code
Get the render function
/** const stringCode = 'with(this){return */ const stringCode =' with(this){return _c('div',_c('p',[_v(message)])])}` const render = new Function(stringCode)Copy the code
To view different instructions, interpolation, and JS expressions, use vue-template conversion
Const compiler = require(' vUE -template-compiler') // Interpolate const template = '<p>{{message}}</p>' const result = compiler.compile(template) console.log(result.render) // with(this){return _c('p',[_v(_s(message))])}Copy the code
Vue source code to find the meaning of the abbreviation function
The template-compiled source code can be viewed in the VUe-template-Compiler package
Function installRenderHelpers(target) {target._c = createElement // mark v-once target._o = markOnce // convert to Number Target. _n = toNumber // converts toString target._s = toString // render v-for target._l = renderList // render normal slots and scope slots target._t = RenderSlot // renderStatic node target._m = renderStatic through staticRenderFns // get filter target._f = resolveFilter // check keyboard event keycode _k = checkKeyCodes target._b = bindObjectProps // createTextVNode target._v = createTextVNode // create empty vnode target._e = CreateEmptyVNode target._u = resolveScopedSlots target._g = bindObjectListenersCopy the code
review
Vue scaffolding will use vue-loader to do template compilation (precompilation) in the development environment
Parsing is done by truncating the string in small chunks, maintaining a stack to hold the DOM depth, and parsing a complete AST when all strings have been truncated
The optimization process is to recursively mark all nodes to indicate whether they are static or not, and then recursively mark the static root node again
The code generation phase is the execution of a string of code through recursive generation functions that call different generation methods based on different node types
reference
Vue template AST in detail
vue-template-compiler