[Vue2.0 source code learning] template compilation - template parsing stage (summary)

1 Introduction

Up to now, the three major stages of template compilation have been introduced. Next, in this article, we will review and sort out the whole process of template compilation from a macro perspective.

First of all, we need to figure out what the ultimate purpose of template compilation is: to convert the template written by the user into a function that Vuecan be called by the instance when it is mounted render. Or you can simply understand it like this: template compilation is a machine that inputs template strings to it, and it outputs corresponding renderfunctions.

As long as we keep the ultimate goal of template compilation in mind, all the changes in template compilation are aimed at achieving this goal.

Next, let's sort out the entire process of template compilation from a macro perspective.

2. Overall process

As mentioned above, template compilation is to convert templates into functions Vuethat can be called by instances when they are mounted render. Then we Vuestart with the instance mount and push forward step by step. We know that Vuewhen an instance is mounted, it will call the global instance method - $mountmethod (this method will be described in detail later). So let's take a look at $mountthe method first, as follows:

Vue.prototype.$mount = function(el) {
    
    
  const options = this.$options;
  // 如果用户没有手写render函数
  if (!options.render) {
    
    
    // 获取模板,先尝试获取内部模板,如果获取不到则获取外部模板
    let template = options.template;
    if (template) {
    
    
    } else {
    
    
      template = getOuterHTML(el);
    }
    const {
    
     render, staticRenderFns } = compileToFunctions(
      template,
      {
    
    
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      },
      this
    );
    options.render = render;
    options.staticRenderFns = staticRenderFns;
  }
};

As can be seen from the above code, first Vueobtain the option from the attribute option of the instance render, if not obtained, it means that the user does not have a handwritten renderfunction, then at this time, as mentioned in the previous article, you need to Vueconvert the template into rendera function yourself. Then get the template, first try to get the internal template, if you can't get it, get the external template. Finally, call compileToFunctionsthe function to convert the template into rendera function, and then renderassign the function to options.render.

Obviously, the core part of the above code is the part that calls compileToFunctionsthe function generation renderfunction, as follows:

const {
    
     render, staticRenderFns } = compileToFunctions(
  template,
  {
    
    
    shouldDecodeNewlines,
    shouldDecodeNewlinesForHref,
    delimiters: options.delimiters,
    comments: options.comments
  },
  this
);

You can get the function by passing the template templateto the function , so how does this function come from?compileToFunctionsrendercompileToFunctions

We found compileToFunctionsthe source of the function through the code jump as follows:

const {
    
     compile, compileToFunctions } = createCompiler(baseOptions);

We found that compileToFunctionsa function is createCompilerone of the return value objects of a function. createCompilerAs the name implies, the function is to create a compiler. Then let's continue to push forward and see createCompilerwhere the function comes from.

createCompilerThe source of the function is located in the source code src/complier/index.jsfile, as follows:

export const createCompiler = createCompilerCreator(function baseCompile(
  template: string,
  options: CompilerOptions
): CompiledResult {
    
    
  // 模板解析阶段:用正则等方式解析 template 模板中的指令、class、style等数据,形成AST
  const ast = parse(template.trim(), options);
  if (options.optimize !== false) {
    
    
    // 优化阶段:遍历AST,找出其中的静态节点,并打上标记;
    optimize(ast, options);
  }
  // 代码生成阶段:将AST转换成渲染函数;
  const code = generate(ast, options);
  return {
    
    
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  };
});

It can be seen that the function is returned createCompilerby calling the function, and the function receives a function as a parameter. Let's take a closer look at this function, which is what we call the main function of the three stages of template compilation. Pass this function to the function to get the function, then let's push forward and see how the function is defined.createCompilerCreatorcreateCompilerCreatorbaseCompilebaseCompilecreateCompilerCreatorcreateCompilercreateCompilerCreator

createCompilerCreatorThe definition of the function is located in the source code src/complier/create-compiler.jsfile, as follows:

export function createCompilerCreator(baseCompile) {
    
    
  return function createCompiler(baseOptions) {
    
    };
}

It can be seen that calling createCompilerCreatorthe function will return createCompilerthe function, and we can also see createCompilerthe definition of the function, as follows:

function createCompiler(baseOptions) {
    
    
  function compile() {
    
    }
  return {
    
    
    compile,
    compileToFunctions: createCompileToFunctionFn(compile)
  };
}

createCompilerA sub-function is defined inside the function , compileand an object is returned at the same time. The second attribute of this object is what we saw at the beginning compileToFunctions, and its value corresponds to createCompileToFunctionFn(compile)the return value of the function. Then let's push forward and see createCompileToFunctionFn(compile)what the function looks like.

createCompileToFunctionFn(compile)The source of the function is located in the source code src/complier/to-function.jsfile, as follows:

export function createCompileToFunctionFn(compile) {
    
    
  return function compileToFunctions() {
    
    
    // compile
    const res = {
    
    };
    const compiled = compile(template, options);
    res.render = createFunction(compiled.render, fnGenErrors);
    res.staticRenderFns = compiled.staticRenderFns.map(code => {
    
    
      return createFunction(code, fnGenErrors);
    });
    return res;
  };
}

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

It can be seen that createCompileToFunctionFnthe function can be obtained by calling compileToFunctionsthe function. Finally, it is pushed to the end. It turns out that the first compileToFunctionsfunction call is defined here, so let's take a look at compileToFunctionswhat is done inside the function.

compileToFunctionsThe function passed in will be called inside compilethe function, and this compilefunction is createCompilera sub-function defined inside the function, as follows:

function compile(template, options) {
    
    
  const compiled = baseCompile(template, finalOptions);
  compiled.errors = errors;
  compiled.tips = tips;
  return compiled;
}

compileThe function passed in will be called inside the function , baseCompileand this baseCompilefunction is the main line function of the three stages of template compilation as we call it, as follows:

function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
    
    
  // 模板解析阶段:用正则等方式解析 template 模板中的指令、class、style等数据,形成AST
  const ast = parse(template.trim(), options)
  if (options.optimize !== false) {
    
    
    // 优化阶段:遍历AST,找出其中的静态节点,并打上标记;
    optimize(ast, options)
  }
  // 代码生成阶段:将AST转换成渲染函数;
  const code = generate(ast, options)
  return {
    
    
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }

So now it is clear that the compileToFunctionsfunction called at the beginning calls compilethe function, and the function compileis called inside baseCompilethe function, and baseCompilethe function returns renderthe function string generated in the code generation stage. So compileToFunctionscalling compilethe function inside the function can get the generated renderfunction string, and then pass the function string to the function compileToFunctionsinside the function to become a real function and return it, and finally assign it to . In order to facilitate a better understanding, we have drawn a flow chart of the above process, as follows:rendercreateFunctionrenderoptions.render

insert image description here

The above is the overall process of template compilation.

3. Overall map

insert image description here

Guess you like

Origin blog.csdn.net/weixin_46862327/article/details/131744436
Recommended