上篇,介绍了组件部分-组件的编译,主要涉及以下几部分:
html->render->vnode
createComponent
本篇,组件部分-组件的生命周期;
Vue.component
方法:Vue.component
方法用于注册 Vue 全局组件;Vue.component
内部会使用Vue.extend
方法对组件定义 definition 进行处理,返回组件的构造函数;Vue.options.components
中,便于后续的使用;mergeOptions(vm.constructor.options, options)
将全局组件定义合并到局部组件定义上;__proto__
继续向上查找全局组件定义;到这里,就完成了组件虚拟节点的创建;
注意:所有的组件都是通过 Vue.extend 方法来实现的;
接下来,根据组件的虚拟节点创建组件的真实节点,之后再进行挂载;
todo 又读了一遍,感觉写的还不好, 先做标记:
1,全局组件注册:包含 Vue.component 和 extend 两部分
...
后续仍需改善
当组件初始化时,执行初始化的回调,即hook钩子;在不同的地方执行不同的钩子
new Ctor()
扩展组件 data 属性,为组件添加生命周期钩子函数:
/*** 创造组件的虚拟节点 componentVnode*/
function createComponent(vm, tag, data, children, key, Ctor) {if(isObject(Ctor)){// 获取 Vue 并通过 Vue.extend 将对象处理成为组件的构造函数Ctor = vm.$options._base.extend(Ctor)}// 扩展组件的生命周期data.hook = {init(){let child = new Ctor({});child.$mount();},prepatch(){},postpatch(){}}// 创建vnode时,组件是没有文本的,需要传入 undefined// 注意:组件没有孩子,组件的孩子就是插槽,将 children 放到组件的选项中let componentVnode = vnode(vm, tag, data, undefined, key, undefined, {Ctor, children, tag});return componentVnode;
}
4,创建组件的真实节点
如何去找组件的真实节点?
create 方法完成之后,会继续执行 patch 方法,
createElm 方法,是真正将虚拟节点转化为真实节点的位置;
由于组件的加入,此时的 createElm 方法中可能会包含 componentOptions:
第一次:id 为 app 的真实节点
第二次:my-button 组件
在原 createElm 方法中,是直接通过document.createElement(tag)
创建标签,现在还需要添加对组件类型的处理,通过createComponent
创建组件的真实节点:
/*** 创造组件的真实节点* @param {*} vnode */
function createComponent(vnode) {console.log(vnode)
}// 面试:虚拟节点的实现?如何将虚拟节点渲染成真实节点
export function createElm(vnode) {// 虚拟节点必备的三个:标签,数据,孩子let { tag, data, children, text, vm } = vnode;debugger;// vnode.el:绑定真实节点与虚拟节点的映射关系,便于后续的节点更新操作if (typeof tag === 'string') { // 元素// 组件的处理if(createComponent(vnode)){ // 将组件的虚拟节点,创建成为组件的真实节点}// 处理当前元素节点vnode.el = document.createElement(tag) // 创建元素的真实节点updateProperties(vnode, data) // 处理元素的 data 属性// 处理当前元素节点的儿子:递归创建儿子的真实节点,并添加到对应的父亲中children.forEach(child => { // 若不存在儿子,children为空数组vnode.el.appendChild(createElm(child))});} else { // 文本:文本中 tag 是 undefinedvnode.el = document.createTextNode(text) // 创建文本的真实节点}return vnode.el;
}
在 createComponent 方法中,在 data 属性上进行组件生命周期的扩展:
// src/vdom/index.jsfunction createComponent(vm, tag, data, children, key, Ctor) {if(isObject(Ctor)){// 获取 Vue 并通过 Vue.extend 将对象处理成为组件的构造函数Ctor = vm.$options._base.extend(Ctor)}// 扩展组件的生命周期data.hook = {init(){},prepatch(){},postpatch(){}}// 创建vnode时,组件是没有文本的,需要传入 undefined// 注意:组件没有孩子,组件的孩子就是插槽,将 children 放到组件的选项中let componentVnode = vnode(vm, tag, data, undefined, key, undefined, {Ctor, children, tag});return componentVnode;
}
在 createComponent 方法中,尝试获取生命周期 hook,如果 hook 存在说明是组件,再拿到 init 方法,处理虚拟节点 vnode:
/*** 创造组件的真实节点* @param {*} vnode */
function createComponent(vnode) {console.log(vnode);let i = vnode.data;if((i = i.hook)&&(i = i.init)){ // 最后 i 为 init 方法i(vnode); // 将 vnode 传入 init 方法}
}
先把 hook 赋值给 i,再把 init 赋值给 i,
最终 i 为 init 方法;
组件的初始化,即 new 组件的构造函数,此时就会进入 init 方法:
todo
4,创建组件的真实节点
通过 new Ctor 拿到组件的实例,并调用组件的 $mount
方法,生成一个 $el
;
vnode.componentInstance = new Ctor()
vnode.componentInstance.$el
即为组件渲染后的结果;
5,将组件的 vnode.componentInstance.$el
插入到父标签中
6,当组件实例化 new Ctor() 时,会进行组件的初始化,此时会为组件添加一个独立的渲染过程,为每个组件生成各自的渲染 watcher;当组件更新时,只需要更新自己组件对应的渲染 watcher 即可;所以性能是非常高的,因为在组件渲染时,组件对应的属性会收集自己的渲染 watcher
本篇,介绍了组件部分-组件的生命周期,主要涉及以下几部分:
todo
下一篇,组件部分-生成组件的真实节点
今天公司值班没有网络,先怼上明天周末再修改,见谅~
备注:今天更新了一小部分,这篇还有太多问题,明天继续搞定,见谅~