我们在使用vue的时候,会拆分许多组件,一般按照路由拆分业务组件,还有我们自己封装的UI组件。 在vue中组件的使用比较多,简单介绍下vue组件化的初始流程。
1. 测试代码
const vm = new Vue({
el: '#app',
components:{ // vm.$options.components['my'] = {my:模板}
my:{
template:'<div>my-component</div>'
}
}
});
2. 组件初始化流程
1. 注册组件的时候会将组件添加到$options.components中去,如vm.$options.components['my'] = {my:模板}
2. 创造组件的虚拟节点 createComponent {tag:'my',data:{hook:{init}},componentOptions:{Ctor:Vue.extend( {my:模板})}}
3. - 创造真实节点的 createComponent init -> new 组件().$mount() -> vm.componentInstance
4. - vm.$el 插入到父元素中
扩展 -同步组件的父子组件的生命周期
- 父组件 beforeCreate
- 父组件 created
- 父组件 beforeMount
子组件beforeCreate
子组件 created
子组件 beforeMount
子组件mounted
- 父组件 mounted
2.1. vue初始化
vm.options.el)挂载的时候判断是否有template,然后去编译。 编译的流程有三步: 解析,转换成ast,生成代码
模板编译生成的render函数,可以看到_c函数和with方法
2.2编译完成后, mount.call(this, el, hydrating) // 调用挂载
// public mount method
Vue.prototype.$mount = function (
el,
hydrating
) {
el = el && inBrowser ? query(el) : undefined;
// 做组件的挂载
return mountComponent(this, el, hydrating)
};
2.3_update方法内部调用patch方法去创建节点
vm.$el = vm.__patch__(prevVnode, vnode);
patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly);
// 组件的挂载 return mountComponent(this, el, hydrating)
2.4 createElm 内部判断是元素createElement,组件 createComponent 或者文本createTextNode,进行不同的操作
child.$mount(hydrating ? vnode.elm : undefined, hydrating);
2.5组件加载完后componentInstance已经赋值,说明组件实例可复用,插入到父元素
- 将父元素插入到body
- 创建 vue-component-1-my
- 创建h1 插入到component中
- 创建text ,插入到h1中
小结
1.组件初始化整个流程比较长,在源码里面打断点记录了这些关键节点和方法调用。
2.为什么有些钩子的执行是先子后父亲,有些是先父后子 组件渲染是如何渲染的?
// 遇到父组件就先渲染父组件
<div id="app">
// 遇到子组件就渲染子组件
<my-button >
// 先渲染子组件后 完成才能渲染完毕父组件
</div>