The past belongs to death, the future belongs to yourself

preface

It is questions that inspire us to learn, to practice, to observe. For problems, I used to follow the thinking of what 👉 why 👉 how to do.

Template compilation, before I do not understand, at least I see the word compilation, feel very powerful appearance. The entry describes how compilation works like this:

The principle of compiler is an important professional course in computer science, which aims to introduce the general principle and basic method of compiler construction. Topics include language and grammar, lexical analysis, syntax analysis, syntax-guided translation, intermediate code generation, storage management, code optimization, and object code generation. The principle of compiler is an important professional course for computer majors. The course of Compiler principle is a compulsory course for students majoring in computer science and the basic and core course for colleges and universities to train computer professionals. It is also one of the most difficult and challenging courses in computer science. Compilation principle course content is mainly principle nature, highly abstract.

😧 um… Why didn’t I have this course in my college course? Forget it. Even if I did, I would never learn it if I was so naughty. Simple read again, let my eyes a bright 😃 is the most difficult and the most challenging learning ability of these keywords, I am an ordinary person, most of us are ordinary people, understand me. Hey hey, give up, I want to go back to my bed.

No, rabbit rabbit is an ideal person, determined to do better than most people, than the rest of the few people are also good, it is not people do. Struggle, get up, I can learn 😤. As we have seen above, the compilation principle includes lexical analysis, grammar analysis, code optimization, object code generation, etc. Therefore, in VUE, the template compilation process is roughly divided into three stages based on this, namely template parsing stage, optimization stage and code generation stage.

The body of the

On vue’s official API, we’ll see the following description in some places:

So what isThe full version? So before we get started, let’s take a lookvueBuild of.

build

Full version

The full version, the Runtime + Compiler version, is a complete version that contains both the compiler and the runtime. The full version includes a compiler that automatically compiles the template string in the template option into the code for the render function. The full version is usually only used when it is introduced as a CDN:

new Vue({
  template: '<div>{{ hi }}</div>'
})
Copy the code

We will do this in the js corresponding to each HTML file. Compilation is a performance-intensive process, and the full version is bulky (about 30% larger than the runtime version) because of the compiled flow code. When using the CDN chained form, the browser will download resources when parsing script tags, which will be time-consuming if the resources are large, and template compilation will be carried out after downloading resources, so the performance will be greatly reduced.

Runtime version

The runtime version, the Runtime version, removes the code for the compilation process. So when we use the run-time version, we don’t need to compile the template we wrote, except for the render function we wrote by hand? You need to compile at any time. Most of our work is to use a single file component (*.vue) for rapid development. In this engineering development mode, vue-loader will compile the template template in the *.vue file (also compile the style). Compiling this part of the vue-loader plug-in helps us do that.

The runtime version incorporates the Vue compilation phase of the template into the WebPack build process, which not only reduces the volume of production code, but also greatly improves runtime performance.

Therefore, in order to fully learn the source code, we should read the complete version of runtime + Compiler code.

What is template compilation

The template in a single file component is the content similar to native HTML written in the tag. Template compilation is the process of compiling the template to produce the render function.

Why template compilation

Our template is described above as similar to native HTML, similar because we use template interpolation, directives, other components, and so on in development. Template compilation takes care of the non-native HTML content and eventually generates a render function that can be called to generate the template’s virtual DOM so that DOM Diff can be performed later to update the render view.

Template Compilation Process

The template compilation phase only exists in the full version and occurs between created and beforeMount in the lifecycle. If the render function is present, the template option is not defined. If the render function is present, the template is not compiled. If your render is defined like this:

import App from './App'

new Vue({
  el: '#app',
  router,
  store,
  render: h= > h(App)
})
Copy the code

So template compilation takes place during the patch phase when Vue subclass is instantiated (see the previous Vue2.0 source code reading plan (iv) — virtual DOM), compilation is a process that must be performed at any time.

The specific process of template compilation can be roughly divided into three stages:

  1. Template parsing stage: a bunch of template strings are parsed into abstract syntax trees by means of reAST(The parser);
  2. Optimization phase: traversalAST, find the static nodes and mark them (The optimizer);
  3. Code generation phase: willASTConvert to render functions (Code generator);

From the source can be seen:

// source code: / SRC /complier/index.js

export const createCompiler = createCompilerCreator(function baseCompile (template: string, options: CompilerOptions) :CompiledResult {
  // Template parsing stage: Parses directives, class, style and other data in the template template using regular methods to form an AST
  const ast = parse(template.trim(), options)
  if(options.optimize ! = =false) {
    // Optimization phase: walk through the AST to find the static nodes and mark them;
    optimize(ast, options)
  }
  // Code generation phase: convert AST to render function;
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})
Copy the code

Template parsing phase

This stage is parsing the template into an AST, so what is an AST?

In computer science, an AbstractSyntaxTree (AST), or simply Syntax tree (Syntax tree), is an abstract representation of the syntactic structure of source code. It represents the syntactic structure of a programming language as a tree, with each node in the tree representing a structure in the source code.

In layman’s terms, an AST in vUE is a tree-like JS object that contains key valid information attributes in a template. Let’s take a look at this online site:

The parsing phase is still quite complex, so I’ll just comb through the main implementation process:

All related to compile the code under the/SRC/complier, parser defined in/SRC/complier/parser/index in js:

/ / pseudo code
export function parse(template, options) {
   // ...
  parseHTML(template, {
    warn,
    expectHTML: options.expectHTML,
    isUnaryTag: options.isUnaryTag,
    canBeLeftOpenTag: options.canBeLeftOpenTag,
    shouldDecodeNewlines: options.shouldDecodeNewlines,
    shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
    shouldKeepComment: options.comments,
    // This function is called when the start tag is parsed
    start (tag, attrs, unary) {
    
    },
    // This function is called when the end tag is parsed
    end () {
    
    },
    // This function is called when text is parsed
    chars (text) {
        if (text) parseText(text, delimiters)
    },
    // This function is called when a comment is parsed
    comment (text) {

    }
  })
  return root
}
Copy the code

Parse calls the parseHTML function within the main function, which, as its name implies, parses template strings. / SRC /complier/parser = html-parser.js; text-parser.js; filter-parser.js; They correspond to AN HTML parser (parseHTML), a text parser (parseText), and a filter parser (parseFilters).

The whole process of parsing is: take the HTML parser as the main line, first use the HTML parser to parse the entire template, in the process of parsing if you encounter the text content, then call the text parser to parse the text, if you encounter the text contains a filter then call the filter parser to parse.

In the source code, you’ll see parseText called when the chars hook function is triggered in parseHTML, and parseFilters called in the definition of parseText.

HTML parser internal operation process: through the loop parsing template, with different regular expressions will text, HTML comments, conditional comments, DOCTYPE, start tag, end tag and other different content from the template string out of a parsing, for different cases are different processing, until the whole template is parsed.

During matching, advance is used to advance the entire template string to the end of the string:

function advance (n) {
    index += n
    html = html.substring(n)
}
Copy the code
  • Called when the start tag is parsedstartFunction to generate the element typeASTNode, the code is as follows:
start (tag, attrs, unary) {
	let element = createASTElement(tag, attrs, currentParent)
}

export function createASTElement (tag,attrs,parent) {
  return {
    type: 1,
    tag,
    attrsList: attrs,
    attrsMap: makeAttrsMap(attrs),
    parent,
    children: []}}Copy the code
  • Called when the end tag is parsedendFunction:
end (tag, start, end) {
  const element = stack[stack.length - 1]
  closeElement(element) // Do not do not understand
}
Copy the code
  • Called when text is parsedcharsFunction to generate text typeASTNodes:
chars (text) {
  if(Text is dynamic text with variables){let element = {
      type: 2.expression: res.expression,
      tokens: res.tokens,
      text
    }
  } else {
    let element = {
      type: 3,
      text
    }
  }
}
Copy the code

Comment is called when a comment is parsed to generate an AST node of the comment type:

comment (text: string) {
  let element = {
    type: 3,
    text,
    isComment: true}}Copy the code

From the pseudo-code, we can see that there are three types of AST element nodes: type 1 means a normal element, type 2 means an expression, and type 3 means plain text.

Note when parsing: The AST nodes we created above are created separately and dispersed, whereas real DOM nodes are hierarchical. How do we ensure the AST node hierarchy, i.e. how do we build the AST nodes in the tree?

The solution to this problem is to define a stack at the beginning of the HTML parser. When the start tag is parsed, the start hook function is called to push the parsed start tag onto the stack. When parsing a closing tag, call the end hook function to pop the start tag corresponding to the parsed end tag from the top of the stack. The label left at the top of the stack is the parent node of the pop-up label.

The processing flow of improperly closed labels is as follows:

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

Resolves to < div > stack, resolves to < p > into the stack and resolves to < span > stack, resolves to < / p > stack, but the top is < span > at this time, so long span tag is not properly closed, the console will be thrown caution: tag has no matching end tag.

The optimization phase

After AST is built, VUE is also optimized, that is, marking static nodes (< P> I am static node

). In the patch phase, the comparison of static nodes will be directly skipped to improve performance.

The principle of marking static nodes is simple: start with the root node, mark it as a static node, then see if the root node is an element node, recurse down its children, if there are any children, recurse down until all the nodes are marked. Whenever a child is not static during recursion, its parent becomes static as false.

The premise for marking the static root node is:

  • The node itself must be static;
  • You must have child nodeschildren;
  • Child nodes cannot have just one text node;

Otherwise, the cost of optimizing it will outweigh the benefits of optimizing it. Tagging works the same way as finding static nodes.

Code generation phase

All you need to do in this stage is generate the Render function from the optimized AST. This is also a recursive process, recursing from top to bottom for each node in the AST, creating different VNode types based on the different AST node types. The VNode is actually generated by calling createElement (the first argument to the Render function).

After iterating through the AST, wrap with with and output a string:

` with(this){ reurn _c( 'div', { attrs:{"id":"NLRX"}, } [ _c('p'), [ _v("Hello "+_s(name)) ] ]) } `
Copy the code

Finally, place the Function string as an argument in the new Function(code) :

res.render = createFunction(compiled.render, fnGenErrors)

function createFunction (code, errors) {
  try {
    return new Function(code)
  } catch (err) {
    errors.push({ err, code })
    return noop
  }
}
Copy the code

Vnodes are generated when we call render.

At this point, the template compilation process is complete. Personally, this process is more complicated, understanding the general idea is ok, dead knock code is useless, framework is learning ideas.

conclusion

This article is more inclined to notes, summary, and added their own understanding of learning, we come together ah!!

Reference: Vue source code series -Vue Chinese community Vue. Js technology revealed