preface
Project address in this chapter
- throughVue.componentMethod creates a method based on
Object.create(Vue.prototype)
- Whether new Vue() has Componets to merge, and can be passed by (
__proto__
Chain call - Generate vNode virtual nodes and perform special processing on components
Data.hook = {init () {}}
- Generate real DOM nodes
new Componet().$mount() -> vm.$el
To the chase
The sample
<div id="app">
<my-button></my-button>
</div>
<script>
Vue.component('my-button', {
template: '<button>hello</button>'
})
let vm = new Vue({
components: {
'my-button': {
template: '<button>world</button>',
}
}
})
vm.$mount('#app')
</script>
Copy the code
Vue.component mergeOptions mergeOptions methods
/** stores global configuration */ with which each component is initialized
Vue.options = {}
/** Subclasses can find Vue */ via _base
Vue.options._base = Vue
Vue.options.components = {}
Vue.component = function (id, definition) {
definition = this.options._base.extend(definition)
this.options.components[id] = definition
}
/** Core method */
/** returns an object that inherits Vue class */
Vue.extend = function (opts) {
const Super = this
const Sub = function VueComponent(options) {
this._init(options)
}
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
// Only merge with vue. options if global options have something to merge like vue. mixin...
Sub.options = mergeOptions(Super.options, opts)
return Sub
}
/** auxiliary methods **/
/ * * *@description Vue.com component hook function */
strats.components = function(parentVal, childVal) {
let options = Object.create(parentVal)
if (childVal) {
for (let key in childVal) {
options[key] = childVal[key]
}
}
return options
}
/** Store various policies such as life cycle... * /
let strats = {}
/ * * *@description Merge the Vue. Options * /
export function mergeOptions(parent, child) {
const options = {}
for (let key in parent) {
mergeField(key)
}
for (let key in child) {
if (parent.hasOwnProperty(key)){
continue
}
mergeField(key)
}
function mergeField(key) {
let parentVal = parent[key]
let childVal = child[key]
if (strats[key]) {
options[key] = strats[key](parentVal, childVal)
} else {
if(isObject(parentVal) && isObject(childVal)) { options[key] = { ... parentVal, ... childVal } }else {
options[key] = parentVal || childVal
}
}
}
return options
}
Copy the code
Create a hook function to generate a VNode, add a new Ctor() component instance to the vnode, execute this hook function to generate a real DOM to return the component instance on the vNode
- Generate a vNode file
/ * * *@description Is it an object */
export function isObject(data) {
return typeof data === 'object'&& data ! = =null
}
/ * * *@description Is it a native label */
export function isReservedTag(str) {
let reservedTag = 'a,div,span,ul,li,p,img,button'
return reservedTag.includes(str)
}
/ * * *@description Create a vnode */ for the label
export function createElement(vm, tag, data = {}, ... children) {
/ ** see if ** tag is a component
if (isReservedTag(tag)) {
return vnode(vm, tag, data, data.key, children, undefined)}else {
const Ctor = vm.$options.components[tag]
return createComponent(vm, tag, data, data.key, children, Ctor)
}
}
/ * * *@description Create a vNode */ for the component
function createComponent(vm, tag, data, key, children, Ctor) {
// The component is not a constructor
if (isObject(Ctor)) {
Ctor = vm.$options._base.extend(Ctor)
}
data.hook = {
init(vnode) {
let vm = vnode.componentInstance = new Ctor({_isComponent: true})
vm.$mount()
}
}
return vnode(vm, `vue-component-${tag}`, data, key, undefined.undefined, {Ctor, children})
}
/ * * *@description Create text vnode */
export function createTextElement(vm, text) {
return vnode(vm, undefined.undefined.undefined.undefined, text)
}
/** Core method */
/ * * *@description Suit vnode * /
function vnode(vm, tag, data, key, children, text, componentOptions) {
return {
vm,
tag,
data,
key,
children,
text,
componentOptions
/ /...}}Copy the code
2. Generate the real DOM
export function patch(oldVnode, vnode) {
// The component does not have an oldVnode
if(! oldVnode) {return createElm(vnode)
}
if (oldVnode.nodeType == 1) {
const parentElm = oldVnode.parentNode
let elm = createElm(vnode)
parentElm.insertBefore(elm, oldVnode.nextSibling)
parentElm.removeChild(oldVnode);
return elm
}
}
/ * * *@description Create the real node of the component */
function createComponent(vnode) {
let i = vnode.data
if ((i = i.hook) && (i = i.init)) {
i(vnode)
}
if (vnode.componentInstance) {
return true}}/** Core method */
/ * * *@description Create the actual node element and assign it to el */ on vNode
function createElm(vnode) {
let { tag, data, children, text, vm } = vnode
if (typeof tag === 'string') {
// Is not a component
if (createComponent(vnode)) {
return vnode.componentInstance.$el
}
vnode.el = document.createElement(tag)
children.forEach(child= > {
vnode.el.appendChild(createElm(child))
})
} else {
vnode.el = document.createTextNode(text)
}
return vnode.el
}
Copy the code