preface
This article mainly handwritten Vue2.0 source code – component principle
In the last post we focused on the Vue Mixin principle and the Vue initialization options and the core API that you know about Vue is componentization and this post focuses on the whole component creation and rendering process and the VUe.extend API is the core of creating components
Applicable group:
1. Want to deeply understand vUE source code better for daily business development
2. Want to write in the resume proficient vUE framework source code (no longer afraid of the interviewer’s serial kill question haha)
3. No time to see the official source code or first look at the source code feel difficult to understand the students
The body of the
<script>
// Global components
Vue.component("parent-component", {
template: '
I am global component
'});// Instantiate Vue
let vm = new Vue({
el: "#app".data() {
return {
aa: 1}; },// render(h) {
// return h('div',{id:'a'},'hello')
// },
template: '
hello Vue{{aa}}
< parent-Component >< child-Component >
'
.// Local component
components: {
"child-component": {
template: '
I'm local component
',}}});</script>
Copy the code
The above illustrates the basic use of global components and local components. In fact, each of our components is a subclass of Vue that can use Vue’s prototype methods
1. Register global components
// src/global-api/index.js
import initExtend from "./initExtend";
import initAssetRegisters from "./assets";
const ASSETS_TYPE = ["component"."directive"."filter"];
export function initGlobalApi(Vue) {
Vue.options = {}; // Global component directive filter
ASSETS_TYPE.forEach((type) = > {
Vue.options[type + "s"] = {};
});
Vue.options._base = Vue; / / _base pointed to the Vue
initExtend(Vue); // Extend method definition
initAssetRegisters(Vue); // The assets registration method contains component directives and filters
}
Copy the code
The initGlobalApi method is used to register Vue’s global methods such as vue.mixin and vue.extend Vue.component
// src/global-api/asset.js
const ASSETS_TYPE = ["component"."directive"."filter"];
export default function initAssetRegisters(Vue) {
ASSETS_TYPE.forEach((type) = > {
Vue[type] = function (id, definition) {
if (type === "component") {
// this points to Vue
// Global component registration
// The child component may also have the extend method vuecomponent.ponent method
definition = this.options._base.extend(definition);
}
this.options[type + "s"][id] = definition;
};
});
}
Copy the code
This.options._base is a global component that uses the vue.extend method to process incoming options and mount them to Vue.options.components
2. Vue. The extend definition
// src/global-api/initExtend.js
import { mergeOptions } from ".. /util/index";
export default function initExtend(Vue) {
let cid = 0; // Unique identification of the component
// Create a subclass that inherits from the Vue parent class to facilitate property extension
Vue.extend = function (extendOptions) {
// Create the subclass constructor and call the initialization method
const Sub = function VueComponent(options) {
this._init(options); // Call the Vue initialization method
};
Sub.cid = cid++;
Sub.prototype = Object.create(this.prototype); // The subclass prototype points to the superclass
Sub.prototype.constructor = Sub; //constructor refers to itself
Sub.options = mergeOptions(this.options, extendOptions); // Merge your options with those of your parent class
return Sub;
};
}
Copy the code
The core idea of vue. extend is to return a subclass of Vue using a prototype-inherited method and to merge the options of the incoming component with the options of the parent class using mergeOptions
3. Component merge policy
// src/init.js
Vue.prototype._init = function (options) {
const vm = this;
vm.$options = mergeOptions(vm.constructor.options, options); / / merge options
};
Copy the code
Remember when we initialized Vue to merge options global components are mounted on Vue.options.components and local components are defined on their own options.components so how do we handle merging global components with local components
// src/util/index.js
const ASSETS_TYPE = ["component"."directive"."filter"];
// Merge policy for component directive filters
function mergeAssets(parentVal, childVal) {
const res = Object.create(parentVal); // For example, if there is a global component with the same name and a local component with its own name, parentVal stands for global component. The component defined by itself is childVal. First, it looks for its own local component and uses its own
if (childVal) {
for (let k inchildVal) { res[k] = childVal[k]; }}return res;
}
// Define the merge policy for components
ASSETS_TYPE.forEach((type) = > {
strats[type + "s"] = mergeAssets;
});
Copy the code
In this case, the method of stereotype inheritance is used to merge components. If the components defined locally are not found, the components defined in the stereotype will be looked up
4. Create a component Vnode
// src/util/index.js
export function isObject(data) {
// Check whether it is an object
if (typeofdata ! = ="object" || data == null) {
return false;
}
return true;
}
export function isReservedTag(tagName) {
// Determine if it is a regular HTML tag
// Define common tags
let str =
"html,body,base,head,link,meta,style,title," +
"address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section," +
"div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul," +
"a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby," +
"s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video," +
"embed,object,param,source,canvas,script,noscript,del,ins," +
"caption,col,colgroup,table,thead,tbody,td,th,tr," +
"button,datalist,fieldset,form,input,label,legend,meter,optgroup,option," +
"output,progress,select,textarea," +
"details,dialog,menu,menuitem,summary," +
"content,element,shadow,template,blockquote,iframe,tfoot";
let obj = {};
str.split(",").forEach((tag) = > {
obj[tag] = true;
});
return obj[tagName];
}
Copy the code
Appellate is a common utility method used during the process of creating a component Vnode
// src/vdom/index.js
import { isObject, isReservedTag } from ".. /util/index";
// create element vnode equal to h in render =>h(App)
export function createElement(vm, tag, data = {}, ... children) {
let key = data.key;
if (isReservedTag(tag)) {
// If it is a common label
return new Vnode(tag, data, key, children);
} else {
// Otherwise it is a component
let Ctor = vm.$options.components[tag]; // Get the constructor of the component
returncreateComponent(vm, tag, data, key, children, Ctor); }}function createComponent(vm, tag, data, key, children, Ctor) {
if (isObject(Ctor)) {
// If not converted to a constructor
Ctor = vm.$options._base.extend(Ctor);
}
// Declare the component's own internal lifecycle
data.hook = {
// The self-initialization method of the component creation process
init(vnode) {
let child = (vnode.componentInstance = new Ctor({ _isComponent: true })); // Instantiate the component
child.$mount(); / / because there is no incoming el properties need to manually mount In order to increase $el on component instance method can be used to generate the component rendering node}};// Component vnode is also called placeholder vnode ==> $vnode
return new Vnode(
`vue-component-${Ctor.cid}-${tag}`,
data,
key,
undefined.undefined,
{
Ctor,
children,
}
);
}
Copy the code
To rewrite the createElement method for a non-normal HTML tag, you need to generate the component Vnode and pass Ctor and children as the last parameter of Vnode componentOptions
$mount(). This method generates the real DOM of the component and mounts it to its own $el property. If you have any questions about this, check out the previous article, hand-write Vue2.0 Source code (three) – initial rendering principle
5. Render the real node of the component
// src/vdom/patch.js
// Patch is used to render and update views
export function patch(oldVnode, vnode) {
if(! oldVnode) {// The component creation process has no EL attribute
return createElm(vnode);
} else {
// Non-component creation is omitted}}// Check whether it is a component Vnode
function createComponent(vnode) {
// Initialize the component
// Create a component instance
let i = vnode.data;
Call data.hook.init to initialize the component. The final component's vnode.ponentInstance.$el is the real DOM rendered by the component
if ((i = i.hook) && (i = i.init)) {
i(vnode);
}
// If the component is instantiated with a componentInstance attribute, then the component is a component
if (vnode.componentInstance) {
return true; }}// Convert the virtual DOM to the real DOM
function createElm(vnode) {
const { tag, data, key, children, text } = vnode;
// Determine whether the virtual DOM is an element node or a text node
if (typeof tag === "string") {
if (createComponent(vnode)) {
// Return the real DOM rendered by the real component if it is a component
return vnode.componentInstance.$el;
}
// The EL attribute of the virtual DOM points to the real DOM to facilitate the subsequent update of the DiFF algorithm
vnode.el = document.createElement(tag);
// Parse virtual DOM properties
updateProperties(vnode);
// If there are children, insert them recursively into the parent node
children.forEach((child) = > {
return vnode.el.appendChild(createElm(child));
});
} else {
// Text node
vnode.el = document.createTextNode(text);
}
return vnode.el;
}
Copy the code
Judge if the component belongs to Vnode then render the component real DOM ==> vnode.ponentInstance.$EL returns
6. Mind mapping of components
summary
So far, Vue component source code has been completed. In fact, each component is a Vue instance will experience init initialization method. It is recommended to learn components before the previous series to understand the component is easier to understand, you can look at the mind map to write the core code ha Feel free to leave a comment if you have any questions or disputes
Finally, if you find this article helpful, remember to like it three times. Thank you very much!
Series of links (will be updated later)
- Handwriting Vue2.0 source code (a) – response data principle
- Handwriting Vue2.0 source code (2) – template compilation principle
- Handwriting Vue2.0 source code (three) – initial rendering principle
- Handwriting Vue2.0 source code (four) – rendering update principle
- Handwriting Vue2.0 source code (five) – asynchronous update principle
- Handwriting Vue2.0 source code (six) -diff algorithm principle
- Handwriting Vue2.0 source code (seven) -Mixin Mixin principle
- Handwriting Vue2.0 source code (eight) – component principle
- Handwriting Vue2.0 source code (nine) – listening attribute principle
- Handwriting Vue2.0 source code (ten) – the principle of computing attributes
- Handwriting Vue2.0 source code (eleven) – global API principle
- The most complete Vue interview questions + detailed answers
- Handwritten vue-Router source code
- Write vuex source code
- Handwriting vue3.0 source code
Shark brother front touch fish technology group
Welcome technical exchanges within the fish can be pushed for help – link