The previous two sections introduced components, from the principles of components to their applications, including asynchronous components and functional components implementation and use scenarios. As is known to all, the component is throughout the Vue design concept, and guide us as we develop the core idea, so in the next few articles, will go back to the content of the component analysis of the source, is first starting from the common dynamic components, including the principle of the inline template, finally will be simply referred to the concept of built-in component, articles for the stage.
12.1 Dynamic Components
Dynamic components I believe most of them will be used in the development process. When we need to switch states between different components, dynamic components can meet our needs very well. The core of this is the use of the Component tag and the IS attribute.
12.1.1 Basic Usage
The example is a basic usage scenario for a dynamic component. When the button is clicked, the view switches between components child1,child2,child3 according to the this.chooseTabs value.
// vue
<div id="app">
<button @click="changeTabs('child1')">child1</button>
<button @click="changeTabs('child2')">child2</button>
<button @click="changeTabs('child3')">child3</button>
<component :is="chooseTabs">
</component>
</div>
// js
var child1 = {
template: '<div>content1</div>',
}
var child2 = {
template: '<div>content2</div>'
}
var child3 = {
template: '<div>content3</div>'
}
var vm = new Vue({
el: '#app', components: { child1, child2, child3 }, methods: { changeTabs(tab) { this.chooseTabs = tab; }}})Copy the code
12.1.2 AST parse
The interpretation of < Component > will be consistent with previous articles, starting from the AST parsing phase. The process will not focus on every detail, but will highlight the differences between the previous processing methods. For the difference in dynamic component resolution, focus on processComponent, which marks the final AST tree as a Component property due to the is attribute on the tag.
// For dynamic component parsingfunctionprocessComponent (el) { var binding; // Get the value of the is attributeif ((binding = getBindingAttr(el, 'is')) {// The ast tree has a component attribute el.component = binding; }if (getAndRemoveAttr(el, 'inline-template') != null) {
el.inlineTemplate = true; }}Copy the code
The final AST tree is as follows:
12.1.3 render function
With the Ast tree, the next step is to generate the executable render function based on the AST tree. Due to the Component property, the render function will branch off genComponent.
Var code = generate(ast, options); // Generate function implementationfunction generate (ast,options) {
var state = new CodegenState(options);
var code = ast ? genElement(ast, state) : '_c("div")';
return {
render: ("with(this){return " + code + "}"),
staticRenderFns: state.staticRenderFns
}
}
functionGenElement (el, state) {··· var code; // Dynamic component branchif(el.component) { code = genComponent(el.component, el, state); }}Copy the code
The logic for handling dynamic components is very simple. When there is no inline template flag (more on this later), the following child nodes are taken and concatenated. The only difference is that the first argument of _c is not a specified string, but a variable representing the component.
// Processing for dynamic componentsfunctionGenComponent (componentName, el, state) {var children = el.inlineTemplate? null : genChildren(el, state,true);
return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : ' ') + ")")}Copy the code
12.1.4 Comparison between Common Components and Dynamic Components
In fact, we can compare the difference between a normal component and a dynamic component in the render function, and see the result clearly.
Render functions for normal components
"with(this){return _c('div',{attrs:{"id":"app"}},[_c('child1',[_v(_s(test))])],1)}"
Render functions for dynamic components
"with(this){return _c('div',{attrs:{"id":"app"}},[_c(chooseTabs,{tag:"component"})],1)}"
To summarize, the difference between a dynamic component and a normal component is:
-
ast
Phase has been addedcomponent
Property, which is the flag of a dynamic component
-
- produce
render
Function stage due tocomponent
Property will be executedgenComponent
Branch,genComponent
The execution functions of dynamic components are treated specially. Unlike normal components,_c
The first argument to is no longer an immutable string, but a specified component name variable.
- produce
-
render
tovnode
Phases are the same process as normal components, except that strings are replaced with variables and have{ tag: 'component' }
thedata
Properties. In the examplechooseTabs
In this case, I’m going to take thetachild1
.
With the render function, the next process from VNode to real node is basically the same as that of ordinary components in terms of process and thinking. In this stage, we can review the analysis of component process introduced before
12.1.5 doubts
Because MY understanding of the source code is not thorough enough, after reading the creation process of dynamic components, I have a question in my mind. From the process analysis of principle, the core of dynamic components is the keyword is, which defines the component as a dynamic component with the Component attribute during compilation. Component doesn’t seem to have much use as a tag. As long as the is keyword is present, setting the tag name to any custom tag can achieve the effect of dynamic component. (component a, componentb). This string exists only in the VNode data property as {tag: ‘component’}. Does that mean that the so-called dynamic component is only due to the unilateral limitation of IS? What’s the significance of the Component tag? (Ask for advice!!)
12.2 Inlining templates
Since dynamic components can be configured with inline-template in addition to is, the principle and design concept of inline templates in Vue can be clarified. Vue has a prominent line on the official website reminding us that inline-template makes the scope of the template more difficult to understand. Therefore, it is recommended to use the Template option whenever possible to define templates, rather than using inline templates. Next, let’s go through the source code to find out why scopes are so difficult to understand.
Let’s start with a simple tweak to the above example, starting with the perspective of use:
// html
<div id="app">
<button @click="changeTabs('child1')">child1</button>
<button @click="changeTabs('child2')">child2</button>
<button @click="changeTabs('child3')">child3</button>
<component :is="chooseTabs" inline-template>
<span>{{test}}</span>
</component>
</div>
// js
var child1 = {
data() {
return {
test: 'content1'
}
}
}
var child2 = {
data() {
return {
test: 'content2'
}
}
}
var child3 = {
data() {
return {
test: 'content3'
}
}
}
var vm = new Vue({
el: '#app',
components: {
child1,
child2,
child3
},
data() {
return {
chooseTabs: 'child1', } }, methods: { changeTabs(tab) { this.chooseTabs = tab; }}})Copy the code
The effect achieved in this example is consistent with the first example in this article, but the biggest difference is that the environment in the parent component can access the environment variables inside the child component. At first glance, it seems incredible. Recall that the parent component had access to the child component in two broad directions:
– 1. The event mechanism is adopted. The child component informs the parent component of the status of the child component through $emit event, so as to achieve the purpose of the parent accessing the child. – 2. Using the scope slot method, the child variable is passed to the parent as props, and the parent receives it through the v-slot syntax sugar. As we have seen before, this method is essentially an event dispatch to notify the parent.
The core reason why a parent component cannot access variables in its child environment is that everything in the parent template is compiled at the parent scope. Everything in a subtemplate is compiled in the subscope. It’s reasonable to wonder if inline templates violate this rule by putting the parent’s content into the child component creation process to compile. Let’s move on:
Going back to the AST parsing phase, the key to dynamic component parsing is the processComponent function’s handling of the IS property. Another key is the inline-template processing, which adds the inlineTemplate property to the AST tree.
// For dynamic component parsingfunctionprocessComponent (el) { var binding; // Get the value of the is attributeif ((binding = getBindingAttr(el, 'is')) {// The ast tree has a component attribute el.component = binding; } // Add the inlineTemplate propertyif (getAndRemoveAttr(el, 'inline-template') != null) {
el.inlineTemplate = true; }}Copy the code
This step also determines that inline-template templates are not compiled in the parent component phase. How does the template pass to the child component? The answer is that the template exists as an attribute, and the value of the attribute is given to the child instance
functionGenComponent (componentName,el,state) {var children = el.inlineTemplate? null : genChildren(el, state,true);
return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : ' ') + ")")}Copy the code
Let’s look at the result of the final render function, where the template exists in the parent component’s inlineTemplate property as {render: function(){···}}.
"_c('div',{attrs:{"id":"app"}},[_c(chooseTabs,{tag:"component",inlineTemplate:{render:function(){with(this){return _c('span',[_v(_s(test))])}},staticRenderFns:[]}})],1)"
The final VNode result also shows that the inlineTemplate object remains in the data property of the parent component.
{data: {inlineTemplate: {render:function() {}
},
tag: 'component'
},
tag: "vue-component-1-child1"
}
Copy the code
With vNodes in place, you come to the final critical step, the process of generating real nodes from vNodes. Starting from the root node, vue-component-1-child1 will go through the process of instantiating the child component. The inlineTemplate property will be processed before instantiating the child component.
functionCreateComponentInstanceForVnode (vnode, parent) {/ / child components of the default option var options = {_isComponent:true, _parentVnode: vnode, parent: parent }; var inlineTemplate = vnode.data.inlineTemplate; // Get the render function and staticRenderFns respectivelyif(isDef(inlineTemplate)) { options.render = inlineTemplate.render; options.staticRenderFns = inlineTemplate.staticRenderFns; } // Perform vue subcomponent instantiationreturn new vnode.componentOptions.Ctor(options)
}
Copy the code
The default option configuration of the child component will fetch the render function of the template based on the inlineTemplate property on the VNode. At this point in the analysis it’s pretty clear. The contents of an inline template are eventually resolved in the child component, so it is not surprising that the scope of the child component is available in the template.
12.3 Built-in Components
Finally, let’s talk about another concept in Vue’s mind, built-in components. In fact, Vue’s official document lists the built-in components, which are component, Transition, Transition-group, keep-alive, slot, etc.
is described in detail in the section on slots, and the section on using Component spends a lot of time on analyzing usage and principles. However, after learning about slot and Component, I began to realize that slots and Components are not really built-in components. ** Built-in components are components that have been globally registered during the source initialization phase. ** While
and
are not treated as a component, there is no component lifecycle. The slot is converted to renderSlot only in the Render phase, and the Component converts the first parameter of createElement from a string to a variable using the is attribute. That’s all. So back to the concept, ** built-in components are components provided by the source code itself, ** so the focus of this section will be on when the built-in components are registered and what are the differences at compile time. This part is just a good introduction. There will be two articles to introduce the implementation principles of keep-alive, transition and Transition-group in detail.
12.3.1 Constructors define components
The Vue initialization phase will add three component objects to the Components property of the constructor. Each component object is written in the same way as we wrote in the custom component process, including render function, life cycle, and various data definitions.
Var KeepAlive = {render:function() {}} var transition = {render:function() {}} var TransitionGroup = {render:function() {}, methods: {}, ···} var builtInComponents = {KeepAlive: KeepAlive}; var platformComponents = { Transition: Transition, TransitionGroup: TransitionGroup }; // extend(Vue.options.components, builtInComponents); // Extend (Vue.options.components, builtInComponents); extend(Vue.options.components, platformComponents);Copy the code
The extend method, which we talked about at the beginning of the series when we analyzed option merging, merges properties on an object into the source object, overriding the same properties.
// Merges the _from object into the to object, overwriting the properties of the to object if the properties are the samefunction extend (to, _from) {
for (var key in _from) {
to[key] = _from[key];
}
return to
}
Copy the code
The Vue constructor ends up with configuration options for three components.
Vue.components = {
keepAlive: {},
transition: {},
transition-group: {},
}
Copy the code
12.3.2 Registering a Built-in Component
Definition alone is not enough. Components need to be used globally and also need to be registered globally, which is actually clear in the in-depth analysis of Vue source code – option merge (below). The most important first step in the initialization of a Vue instance is to merge options. Resource class options such as built-in components have their own options merge policy. Finally, component options on the constructor are registered with the instance’s Compoonents options as a prototype chain (the same goes for directives and filters).
Var ASSET_TYPES = ['component'.'directive'.'filter']; // Define a policy for resource merging asset_types.foreach ()function (type) {
strats[type + 's'] = mergeAssets; // Define the default policy});functionmergeAssets (parentVal,childVal,vm,key) { var res = Object.create(parentVal || null); // Create an empty object with parentVal as prototypeif(childVal) { assertObjectType(key, childVal, vm); / / components, filters, directives option must be for the objectreturnExtend (res, childVal) // Extend (res, childVal)else {
return res
}
}
Copy the code
Key two steps is a var res = Object. The create (parentVal | | null); , which creates an empty object modeled after parentVal, and finally copies the user-defined Component options into the empty object via extend. After the options are merged, the built-in components are thus registered globally.
{
components: {
child1,
__proto__: {
keepAlive: {},
transition: {},
transitionGroup: {}
}
}
}
Copy the code
Finally, let’s take a look at the built-in component object, which does not have a template, but rather a render function. In addition to reducing the performance cost of template parsing, I think the important reason is that the built-in component does not have entities to render. Finally, let’s look forward to the subsequent analysis of the principles of keep-alive and Transition. Please stay tuned.
- Vue source code – Options merge (top)
- Deep analysis of Vue source code – options merge (next)
- Deep analysis of Vue source – data agent, associated child parent components
- Deep analysis of Vue source code – instance mount, compilation process
- Deep analysis of Vue source code – complete rendering process
- Deep analysis of Vue source code – component foundation
- Deep analysis of Vue source code – components advanced
- Deep analysis of Vue source code – responsive system building (on)
- Deep analysis of Vue source code – responsive system construction (in)
- In-depth analysis of Vue source code – responsive system construction (next)
- Deep analysis of Vue source code – come, with me to achieve diff algorithm!
- Deep analysis of Vue source code – uncover Vue event mechanism
- Deep analysis of Vue source – Vue slot, you want to know all here!
- Vue source code – do you understand the V-Model syntax sugar?