1 Introduction
In the previous articles, we introduced the initialization phase of the life cycle. We know that the vm.$mount
method is called after all the work in the initialization phase is completed. The call of this method marks the end of the initialization phase and the entry into the next phase. From the official documentation, As you can see from the life cycle flow chart, the next stage is the template compilation stage. The main work done in this stage is to obtain the template content passed in by the user and compile it into a rendering function.
The template compilation phase does not exist Vue
in all builds, it only exists in the full version (ie vue.js
. This stage does not exist in the runtime version (i.e. vue.runtime.js
). This is because when using vue-loader
or vueify
, *.vue
the template inside the file will be pre-compiled into a rendering function at build time, so compilation is not required, so there is no template compilation. stage, from the initialization stage of the previous step directly to the mounting stage of the next stage.
Here, we need to introduce what is the full version and the runtime version only.
vue
There are two versions built based on the source code, one runtime only
(a version containing only the runtime) and the other runtime + compiler
(a complete version containing both the compiler and the runtime). The only difference between the two versions is that the latter includes a compiler.
-
complete version
A complete
Vue
version includes a compiler and we can usetemplate
options for template writing. The compiler will automaticallytemplate
compile the template string in the option into the code of the rendering function, which isrender
the function in the source code. If you need to compile the template client-side (such as passing a string to antemplate
option, or mounting it on an element and using itsDOM
inner HTML as a template), you will need a version that includes the compiler. as follows:// 需要编译器的版本 new Vue({ template: '<div>{ { hi }}</div>' })
-
Contains only runtime version
The runtime-only version has functions such as creating
Vue
instances, rendering and processingVirtual DOM
, and is basically the complete code except the compiler. There are two applicable scenarios for this version:1. We define the rendering process through handwritten functions in the options
render
. At this time, we do not need to include the compiler version for complete execution.// 不需要编译器 new Vue({ render (h) { return h('div', this.hi) } })
2. Compile with the help
vue-loader
of such compilation tools. When we usewebpack
engineeringVue
development, we often compile files. Although we also use template tags to write code, there is novue-loader
need to use the compiler to take responsibility at this time. The compilation of the template is done, and this process is left to the plug-in to implement.*.vue
template
Vue
Obviously, the compilation process will cause a certain loss in performance, and due to the addition of compiled process code, the Vue
total size of the code is also larger (the runtime version is about 30% smaller than the full version). Therefore, in actual development, we need to compile with tools like this and webpack
merge the compilation phase of the template into the build process. This not only reduces the size of the production environment code, but also greatly improves the runtime performance, killing two birds with one stone. .vue-loader
Vue
webpack
In order to learn the source code completely, this article will analyze what is done in the template compilation phase in the full version.
2. Analysis of template compilation stage
As mentioned above, the difference between the full version and the runtime-only version mainly lies in whether there is a template compilation stage, and whether there is a template compilation stage is mainly reflected in the implementation of the method vm.$mount
. At this point you may have questions: So, $mount
there are two versions of the method? Yes, you can understand it that way, but in the final analysis it is still the same. Let's take a look at each.
2.1 Comparison of two $mount methods
The code containing only the runtime version $mount
is as follows:
Vue.prototype.$mount = function (el,hydrating) {
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating)
};
$mount
After obtaining the element el
corresponding to the option within the method in this version, the function DOM
is directly called mountComponent
to perform the mounting operation. We will introduce this function in detail in the mounting phase.
The complete version of $mount
the code is as follows:
var mount = Vue.prototype.$mount;
Vue.prototype.$mount = function (el,hydrating) {
// 省略获取模板及编译代码
return mount.call(this, el, hydrating)
}
Note that $mount
before the definition of the complete version, the methods Vue
on the prototype $mount
are cached and recorded as variables mount
. At this point you may ask, this $mount
method has not been defined yet, how can it be cached in the first place.
In fact, in the source code, $mount
the method that contains only the runtime version is first defined, and then the complete version of the $mount
method is defined, so the cached mount
variable at this time is the method that only contains the runtime version $mount
.
Why do you do that? As we said above, the difference between the full version and the runtime-only version mainly lies in whether there is a template compilation phase. The runtime-only version has no template compilation phase. After the initialization phase is completed, it directly enters the mounting phase, while the full version is After the initialization phase is completed, it enters the template compilation phase, and then enters the mounting phase. In other words, both versions will eventually enter the mounting stage. $mount
Therefore , after compiling the template in the full version method, you need to go back and call $mount
the method that only contains the runtime version to enter the mounting phase.
This means that in the full version of $mount
the method, the method that only contains the runtime version $mount
is cached and recorded as a variable mount
, and then mount
the method is executed after the template is compiled (that is, the method that only contains the runtime version $mount
).
Therefore, the analysis template compilation stage is actually vm.$mount
the implementation of the method of analyzing the complete version.
2.2 Complete version of vm.$mount method analysis
The complete version of vm.$mount
the method definition is located in the source code src/platforms/web/entry-runtime-with-compiler.js
, as follows:
var mount = Vue.prototype.$mount;
Vue.prototype.$mount = function (el,hydrating) {
el = el && query(el);
if (el === document.body || el === document.documentElement) {
warn(
"Do not mount Vue to <html> or <body> - mount to normal elements instead."
);
return this
}
var options = this.$options;
// resolve template/el and convert to render function
if (!options.render) {
var template = options.template;
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template);
/* istanbul ignore if */
if (!template) {
warn(
("Template element not found or is empty: " + (options.template)),
this
);
}
}
} else if (template.nodeType) {
template = template.innerHTML;
} else {
{
warn('invalid template option:' + template, this);
}
return this
}
} else if (el) {
template = getOuterHTML(el);
}
if (template) {
if (config.performance && mark) {
mark('compile');
}
var ref = compileToFunctions(template, {
outputSourceRange: "development" !== 'production',
shouldDecodeNewlines: shouldDecodeNewlines,
shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this);
var render = ref.render;
var staticRenderFns = ref.staticRenderFns;
options.render = render;
options.staticRenderFns = staticRenderFns;
if (config.performance && mark) {
mark('compile end');
measure(("vue " + (this._name) + " compile"), 'compile', 'compile end');
}
}
}
return mount.call(this, el, hydrating)
};
As you can see from the code, the function can be roughly divided into three parts:
el
Get elements based on the parameters passed inDOM
;- Get the incoming template without the user's handwritten
render
functiontemplate
; - Compile the obtained information
template
intorender
a function;
Next we will analyze them one by one.
el
First, get the element based on the parameters passed in DOM
. as follows:
el = el && query(el);
function query (el) {
if (typeof el === 'string') {
var selected = document.querySelector(el);
if (!selected) {
warn(
'Cannot find element: ' + el
);
return document.createElement('div')
}
return selected
} else {
return el
}
}
Since el
the parameter can be an element or a string type element selector, query
the function is called to obtain the el
corresponding DOM
element. Since the function is relatively simple, it obtains the corresponding elements in different ways query
depending on whether the passed parameter is a string , so we will not introduce it line by line here.el
DOM
el
In addition, there is an additional judgment here, that is, if the corresponding DOM
element is obtained body
or html
element, a warning will be thrown. This is because Vue
the content in the template will be replaced with the el
corresponding DOM
element. If it is body
an or element, the entire document html
will be destroyed after replacement , so it is not allowed to be or . as follows:DOM
el
body
html
if (el === document.body || el === document.documentElement) {
warn(
"Do not mount Vue to <html> or <body> - mount to normal elements instead."
);
return this
}
Then, render
get the incoming template if the user does not have a handwritten function template
; as follows:
if (!options.render) {
var template = options.template;
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template);
/* istanbul ignore if */
if (!template) {
warn(
("Template element not found or is empty: " + (options.template)),
this
);
}
}
} else if (template.nodeType) {
template = template.innerHTML;
} else {
{
warn('invalid template option:' + template, this);
}
return this
}
} else if (el) {
template = getOuterHTML(el);
}
}
template
First, the options passed in by the user are obtained and assigned to the variable template
. If the variable template
exists, then it is judged that if template
it is a string and ##
starts with, it is considered template
to be id
a selector, and the function is called to obtain the element idToTemplate
corresponding to the selector as a template, as follows:DOM
innerHTML
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template);
}
}
}
var idToTemplate = cached(function (id) {
var el = query(id);
return el && el.innerHTML
});
If template
it is not a string, then determine whether it is an DOM
element. If it is, use the DOM
element innerHTML
as a template, as follows:
if (template.nodeType) {
template = template.innerHTML;
}
If it is neither a string nor DOM
an element, a warning will be thrown: prompting the user that template
the option is invalid. as follows:
else {
{
warn('invalid template option:' + template, this);
}
return this
}
If the variable template
does not exist, it means that the user has not passed in an option, and the function is called to obtain the external template template
according to the passed el
parameters , as follows:getOuterHTML
if (el) {
template = getOuterHTML(el);
}
function getOuterHTML (el) {
if (el.outerHTML) {
return el.outerHTML
} else {
var container = document.createElement('div');
container.appendChild(el.cloneNode(true));
return container.innerHTML
}
}
Whether template
the template is obtained from internal options or externally, the template content passed in by the user must be obtained. With the template content, the template can be compiled into a rendering function.
After obtaining the template, the next thing to do is to compile it into a rendering function, as follows:
if (template) {
var ref = compileToFunctions(template, {
outputSourceRange: "development" !== 'production',
shouldDecodeNewlines: shouldDecodeNewlines,
shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this);
var render = ref.render;
var staticRenderFns = ref.staticRenderFns;
options.render = render;
options.staticRenderFns = staticRenderFns;
}
The specific steps of compiling templates into rendering functions have been introduced in detail in the previous article Template Compilation. Here, we will only briefly review them.
Compiling the template into a rendering function is compileToFunctions
performed in a function. This function receives the template string to be compiled and compilation options as parameters, returns an object, and the render
properties in the object are the compiled rendering functions. Finally, the rendering function is set to $options
superior.
3. Summary
This article introduces the second stage in the life cycle - the template compilation stage.
First, two versions of the source code build are introduced Vue
: the full version and the runtime-only version. And we know that the template compilation phase only exists in the full version, and does not exist in the runtime-only version. This is because in the runtime-only version, when using or, the template inside the file will vue-loader
be vueify
built *.vue
when It is precompiled into a rendering function, so no compilation is required, so there is no template compilation stage.
$mount
Then the differences between the two version methods were compared . The difference is $mount
whether template compilation is done in the method. In the method that only contains the runtime version, the element is $mount
obtained DOM
and directly enters the mounting phase. In the complete version, $mount
the template is compiled first, and then $mount
the method that contains only the runtime version is called back to enter the mounting phase.
Finally, we learned that the analysis template compilation stage is actually the analysis of the implementation of the full version of the method. We analyzed vm.$mount
the full version of the method source code line by line. vm.$mount
We know that the work done in this stage is to obtain the internal or external template passed in by the user from el
the options and options passed in by the user, and then compile the obtained template into a rendering function.template