Vue Source Code Learning (3) Componentization Principle
Another core idea of Vue.js is component. The so-called component is to split the page into multiple components, and the CSS, JavaScript, templates, images and other resources that each component depends on are developed and maintained together. Components are resource-independent, components can be reused within the system, and components and components can be nested.
When we use Vue.js to develop actual projects, we write a bunch of components to assemble and generate pages like building blocks. In the official website of Vue.js, we also spend a lot of time introducing what a component is, how to write a component, and the properties and characteristics that a component has.
Next, we will use the Vue-cli initialization code as an example to analyze a process of Vue component initialization.
1 | import Vue from 'vue' |
createComponent
数据驱动那篇博客我们在分析 createElement
的实现的时候,它最终会调用 _createElement
方法,其中有一段逻辑是对参数 tag
的判断,如果是一个普通的 html 标签,像上一章的例子那样是一个普通的 div,则会实例化一个普通 VNode 节点,否则通过 createComponent
方法创建一个组件 VNode。
1 | if (typeof tag = 'string') { |
In our chapter, we pass an App object, which is essentially a’Component ‘type, so it will go to the else logic of the above code and create a’vnode’ directly through the’createComponent ‘method. So let’s take a look at the implementation of the’createComponent’ method, which is defined in the’src/core/vdom/create-component.js’ file:
1 | export function createComponent ( |
It can be seen that the logic of’createComponent 'will also be a little complicated, but it is recommended to analyze the source code only to analyze the core process, and the branch process can be targeted later, so here are three key steps for the component rendering case:
Construct subclasses to construct functions, install component hook functions, and instantiate’vnode '.
Constructor subclass constructor function
1 | const baseCtor = context.$options._base |
When we write a component, we usually create a normal object, using our App.vue as an example. The code is as follows:
1 | import HelloWorld from './components/HelloWorld' |
The export here is an object, so the code logic in’createComponent 'will execute to’baseCorr.extend (Ctor) ‘, where’baseCtor ‘is actually Vue. The definition of this is at the beginning of the initialization stage of Vue. The’initGlobalAPI’ function in’src/core/global-api/index.js’ has this logic:
1 | // this is used to identify the "base" constructor to extend all plain-object |
Careful colleagues will find that the definition here is’Vue.options’, and our’createComponent ‘is’context. $options’, in fact, in’src/core/instance/init.js’ Vue prototype on the _init function has such a logic:
1 | vm.$options = mergeOptions( |
This extends some of the’options’ on Vue to vm. $options, so we can also get the Vue constructor function through’vm. $options._base ‘. We will analyze the implementation of’mergeOptions’ in subsequent chapters. Now we only need to understand that its function is to merge the’options’ of the Vue constructor function and the’options’ passed by the user to the’vm. $options’.
After understanding that baseCtor points to Vue, let’s take a look at the definition of the Vue.extend function in src/core/global-api/extende.js.
1 | /** |
The role of’Vue.extend ‘is to construct a subclass of’Vue’, which uses a very classic prototype inheritance method to convert a pure object into a constructor’Sub ‘inherited from’Vue’ and return, and then to the’Sub ‘The object itself extends some properties, such as extending’options’, adding global APIs, etc.; and initializing the’props’ and’computed ‘in the configuration; finally, the’Sub’ constructor is cached to avoid multiple execution of’Vue.extend ’ Repeated construction of the same subcomponent.
In this way, when we instantiate’Sub ‘, the’this._init’ logic will be executed again. It goes to the initialization logic of the’Vue 'instance, and the logic of instantiating subcomponents will be introduced in a later chapter.
1 | const Sub = function VueComponent (options) { |
Install component hook function
1 | // install component management hooks onto the placeholder node |
We mentioned earlier that the Virtual DOM used by Vue.js refers to an open source library snabbdomOne of its features is that the hook function of various timing is exposed in the patch process of VNode, which is convenient for us to do some extra things. Vue.js also makes full use of this and implements several hook functions in the process of initializing a VNode of Component type:
1 | const componentVNodeHooks = { |
The whole process of installComponentHooks is to merge the hook function of’components VNodeHooks’ into’data.hook ‘, and execute the relevant hook function during the VNode execution of’patch’. The specific implementation will be described in detail later in the introduction of’patch '. Attention should be paid to the merging strategy. During the merging process, if the hook at a certain time already exists in’data.hook ‘, then merge by executing the’mergeHook’ function. This logic is very simple, that is, when it is finally executed, the two hook functions can be executed in turn.
Instantiation
1 | const name = Ctor.options.name || tag |
The last step is very simple, instantiate a’vnode ‘through’new VNode’ and return it. It should be noted that unlike the’vnode ‘of ordinary element nodes, the’vnode’ of the component does not have’children ‘, which is crucial, and we will mention it later in the’patch’ process.
Summary
In this section, we analyzed the implementation of’createComponent ‘and learned that it has three key logics when rendering a component: constructing a subclass to construct a function, installing a component hook function, and instantiating a’vnode’. After’createComponent ‘returns the component’vnode’, it also goes to the’vm._update ‘method, and then executes the’patch’ function. We did a simple analysis of the’patch 'function in the previous chapter, then we will do further analysis in the next section.