1. The concept

Templates use variables, expressions, directives, etc. These syntax does not exist in HTML, so why can vUE implement? This is thanks to template compilation.

Vue templates are compiled during the mount process, when the compile method is executed to convert the contents of the template into actual HTML code. Complie finally generates the render function and waits to be called.

The purpose of template compilation is to generate render functions, which are executed to generate the latest VNode, and finally to render according to the VNode.

2. Compile the template into a rendering function

2.1 Two steps and three parts

Two steps:

  • The template is parsed into an AST(Abstract Syntax Tree)
  • Use AST to generate rendering functions

*** Since static nodes do not always need to be re-rendered, it is necessary to traverse the AST to mark static nodes after the AST is generated and before the render function is generated

The three parts abstracted three modules respectively:

  • Parsers: Templates are parsed into AST
  • Optimizer: Iterate over AST to mark static nodes
  • Code generator: Use AST to generate rendering functions

2.2 the parser

2.2.1 Functions of parsers

Parse the template string into an AST

<div>
  <p>{{name}}</p>
</div>
Copy the code

After the above template is converted into the AST

{
    tag: "div",
    type: 1,
    staticRoot: false,
    static: false,
    plain: true,
    parent: undefined,
    attrsList: [],
    attrsMap: {},
    children: [{
        tag: "p",
        type: 1,
        staticRoot: false,
        static: false,
        plain: true,
        parent: {tag:"div", ... },
        attrsList: [],
        attrsMap: {},
        children: [{
            type:2,
            text:"{{name}}",
            static:false,
            expression:"_s(name)"
        }]
    }]
}
Copy the code

2.2.2 Internal operating principles of the parser

Parsers include: HTML parser, text parser, filter parser

HTML parser: The HTML parser is used to parse HTML. It triggers different hook functions during the parsing process. These hook functions include the start tag hook function, the end tag hook function, the text hook function, and the comment hook function

2.2.2.1 Hook functions

The pseudocode is as follows:

ParseHTML (template,{start(start){// Trigger the function whenever parsing to the start of the tag}, end(){// trigger the function whenever parsing to the end of the tag}, chars(text){// Whenever parsing to the text, }, comment(text){// When parsing a comment, trigger the function}})Copy the code

Ex. :

<div>
  <p>{{name}}</p>
</div>
Copy the code

Trigger hook functions in the following order: start(), start(), chars(), end(), end()

2.2.1.2 Hierarchical relationship

Use stacks to keep track of hierarchies

Trigger the start function –> push the node onto the stack

Trigger the end function –> to pop the node onto the stack

<div> <p> <span> Stack </span> Hierarchy </p> </div>Copy the code

For the current node span:

The root node The parent node
div p

2.3 HTML parser

Parsing an HTML template is a circular process

The code looks at the parsing process

/ / match the beginning of the start tag < const tagStartReg = / ^ < ([a zA - Z_] [/ w \] - *) /; Const tagStartCloseReg = /^\s*(\/?) > /; / / matching label attribute const tagAttrReg = / ^ \ s * ([a - zA - Z_ @ :] [\ - \ \ w:] *) (? : (? (: =)? : (? "] *) ", "([^) | (? : '([^'] *))))? /; / / matching end tag < / as > const tagEndReg = / ^ < \ / ([a zA - Z_] [/ w \] - *) > /; Const textReg = /^[^<]*/; / / global matching {{}} is not {} characters const dataReg = / \ {\ {([^ {}] +) \} \} / g; let stack = []; let currentAst = {}; let root = null; let index = 0; Let template = '<div id="index"><p> Hello, {{MSG}}</p>vue compile </div>' parseHTML(template, {start, end,}) chars }); // template to ast function parseHTML(templates, hooks) { template = templates while (template) { let tagStart = template.indexOf('<'); if (tagStart === 0) { let start = template.match(tagStartReg); / / / "< div" and "div", the index: 0, input: "< div id =" index "> < p > hello, {{MSG}} < / p > vue compiled < / div >", groups: undefined] if (start) { hooks.start(start); }; let end = template.match(tagEndReg); if (end) { step(end[0].length); // The end tag triggers the end hook function returns.end (); }; } else { let text = template.match(textReg); step(text[0].length); Let ast = {type: 'text', value: hooks.chars(text[0].replace(/\s+/, ' ')) } currentAst.children.push(ast); ast.parent = currentAst; } } console.log('root', root); return root; } function start(start) {start let ast = {type: 'tag', name: start[1], attrs: [], children: [] }; // Move the pointer back step(start[0].length); // match attributes let end, attr; while (! (end = template.match(tagStartCloseReg)) && (attr = template.match(tagAttrReg))) { ast.attrs.push({ key: Attr [1], the value: attr [2] | | attr [3] / / double quotation marks is attr [2], when single quotes is attr [3]}); step(attr[0].length); } if (end) { step(end[0].length); // Not autistic and tag if (! end[1]) { if (! root) { root = ast; } else { currentAst.children.push(ast); ast.parent = currentAst; } stack.push(ast); currentAst = ast; }}; } function end() {stack.pop(); currentAst = stack[stack.length - 1]; } // When parsed to the position of the text, Function chars(text) {const result = [] let lastIndex = 0 let match, {{}} while ((match = datareg.exec (text))) {index = mate.index if (index > lastIndex) { result.push(JSON.stringify(text.slice(lastIndex, index))) } let data = match[1].trim() result.push(`${data === null ? "' : data}`) lastIndex = index + match[0].length } if (lastIndex < text.length) { Result.push (json.stringify (text.slice(lastIndex)))} return result.join('+')} function step(length) {index += length; template = template.slice(length); / / 'id = "index" > < p > hello, {{MSG}} < / p > vue compiled < / div >'}Copy the code