Steps for template compilation in vue: convert template template into ast grammar tree (spliced string), wrap ast grammar tree into Render function through new Function + with grammar, generate virtual node, then mount virtual node into DOM tree to generate real DOM.
(1) take template Template Conversion ast Grammar Tree -parserHTML(Regular implementation) (2) Static tagging of static syntax -markUp (3) Regenerate Code Generation render Function returns virtual nodes Note: Try not to use during development template,Because it will template Convert to render Method, which requires compilation at runtime, can cause performance degradation and refer to e-mail compiler Packaged vue The volume will also increase. default.vue In the file template Processing is through vue-loader(Dependent on vue-template-compiler)Processing instead of compiling at runtime
Regular Used // Matching Attributes const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/ const dynamicArgAttribute = /^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/ const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*` const qnameCapture = `((?:${ncname}\\:)?${ncname})` // Match Start Label Start const startTagOpen = new RegExp(`^<${qnameCapture}`) // Match Start Label Closure const startTagClose = /^\s*(\/?)>/ // End of Match Label const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`) const doctype = /^<!DOCTYPE [^>]+>/i
Parse the start node, match to tagName, then loop through the values of the matched attributes, and assemble into a node
function parseStartTag(){ let start = html.match(startTagOpen) if(start && start.length){ const match = { tagName:start[1], attrs: [] } advance(start[0].length) let end,attr while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { advance(attr[0].length) match.attrs.push({ name: attr[1], value: attr[3]|| attr[4]|| attr[5] }) } if(end){ advance(end[0].length) } return match } }
(1 for dom node and 3 for text node)
After matching the start node, truncate the string that has already been matched, and then match the remaining strings. There are several possible scenarios below
(1) The next match is the end tag
Congratulations, this node has been matched perfectly, just pop it out of our stack of records to form a structure below OK, then intercept the matched string at the point and continue matching with the remaining string until all incoming strings are matched once
{ tag: tagName, type: 1, children: [], attrs:attrs, parent: null }
(2) Next match or start tag
This start tag we match to our previous tag must be a parent-child relationship. Remove the top node from the stack, if it has a value, and record it as the parent node of the current node. Remove the child node of the node as the currently matched node. Then intercept the matched string of the point and continue matching with the remaining string. Until all incoming strings are matched once
(3) The next matching is a piece of text, which is very simple. Use regular matching to the position of the first'<'that is, the next dom node. After removing the space from the matched string, the following text node is generated, and then intercept the matched string from the point, and continue matching with the remaining string. Until all incoming strings are matched once
{ text: text, type:3 }
Wait until all incoming strings have been matched to form a dom tree of the relationships between the nodes described by js
{ tag: 'div', parent: null, attrs:[ { name: 'id', value: 'app' } ], children:[ tag: 'span', parent:{ tag: 'div', parent: null, attrs:[ { name: 'id', value: 'app' } ], children:[...] } ] }
As shown in the diagram
Then it's an exciting time to stitch the node into a virtual dom that can be rendered as a render function based on what the tree provides describing the node, process it in the order tagName,attrs,children, and finally form a string similar to the one below
_c("div",{id:"app",style:{"width":"20px"}},_c("p",undefined,_v("hello"+_s(name))),_v("hello"))
It is important to note that when dealing with inline styles, the values of inline styles should be spelled into key-value forms.
if(attr.name === 'style'){ let obj = {} attr.value.split(';').forEach(attrItem => { let [key, value] = attrItem.split(':') obj[key] = value }) attr.value = obj }
You can then convert the stitched strings into render functions with the help of new Function + with
graph TD A[afferent el attribute] -->B{Determine if there is render Method} B -->|yes| C[Rendering render Method] B -->|no| D{Determine if there is template} D -->|yes| E[Rendering template] --> F D -->|no| F[Rendering el Content inside] F -->|no|G{Determines whether a string begins with an arrow} G -->|yes| H[Use regular matching] H --> I{Is Regular Matching a Start Label} I --> |yes| J[Remove label names and attributes separately with a regular]-->T[Push Start Label on Stack]-->M I --> |no| K{Is Regular Matching the End Label}--> |no| L K --> |yes|R[Remove the top label signature from the stack,Form a closed node] -->U[Add one dom node] --> M G --> |no| L[Description begins with text and then] L--> S[Add a text node] --> M[Truncate matched fields to match again]--> F F -->|yes|N[Generative use js describe dom Node's ast Grammar Tree] N --> O[Stitching and wrapping as strings] O --> P[adopt new Function + with Grammar Generation render function]--> C
(The above is sorted out in the process of their own learning, where errors or inaccuracies are welcome to correct)