Article directory
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 Vue
can 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 render
functions.
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 Vue
that can be called by instances when they are mounted render
. Then we Vue
start with the instance mount and push forward step by step. We know that Vue
when an instance is mounted, it will call the global instance method - $mount
method (this method will be described in detail later). So let's take a look at $mount
the 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 Vue
obtain the option from the attribute option of the instance render
, if not obtained, it means that the user does not have a handwritten render
function, then at this time, as mentioned in the previous article, you need to Vue
convert the template into render
a 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 compileToFunctions
the function to convert the template into render
a function, and then render
assign the function to options.render
.
Obviously, the core part of the above code is the part that calls compileToFunctions
the function generation render
function, 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 template
to the function , so how does this function come from?compileToFunctions
render
compileToFunctions
We found compileToFunctions
the source of the function through the code jump as follows:
const {
compile, compileToFunctions } = createCompiler(baseOptions);
We found that compileToFunctions
a function is createCompiler
one of the return value objects of a function. createCompiler
As the name implies, the function is to create a compiler. Then let's continue to push forward and see createCompiler
where the function comes from.
createCompiler
The source of the function is located in the source code src/complier/index.js
file, 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 createCompiler
by 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.createCompilerCreator
createCompilerCreator
baseCompile
baseCompile
createCompilerCreator
createCompiler
createCompilerCreator
createCompilerCreator
The definition of the function is located in the source code src/complier/create-compiler.js
file, as follows:
export function createCompilerCreator(baseCompile) {
return function createCompiler(baseOptions) {
};
}
It can be seen that calling createCompilerCreator
the function will return createCompiler
the function, and we can also see createCompiler
the definition of the function, as follows:
function createCompiler(baseOptions) {
function compile() {
}
return {
compile,
compileToFunctions: createCompileToFunctionFn(compile)
};
}
createCompiler
A sub-function is defined inside the function , compile
and 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.js
file, 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 createCompileToFunctionFn
the function can be obtained by calling compileToFunctions
the function. Finally, it is pushed to the end. It turns out that the first compileToFunctions
function call is defined here, so let's take a look at compileToFunctions
what is done inside the function.
compileToFunctions
The function passed in will be called inside compile
the function, and this compile
function is createCompiler
a 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;
}
compile
The function passed in will be called inside the function , baseCompile
and this baseCompile
function 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 compileToFunctions
function called at the beginning calls compile
the function, and the function compile
is called inside baseCompile
the function, and baseCompile
the function returns render
the function string generated in the code generation stage. So compileToFunctions
calling compile
the function inside the function can get the generated render
function string, and then pass the function string to the function compileToFunctions
inside 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:render
createFunction
render
options.render
The above is the overall process of template compilation.