写文章不容易,点个赞呗兄弟 专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧 研究基于 Vue版本 【2.5.17】node
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧bash
【Vue原理】Component - 源码版 之 挂载组件DOM dom
由这篇文章 从模板到DOM的简要流程 函数
咱们知道,在生成 VNode 以后,下一步就是根据 VNode 生成DOM而后挂载了学习
在本文开始以前你能够先看 Component - 白话版 先总体了解下componentui
如今开始咱们的正文this
上一篇文章 Component - 建立组件VNode ,咱们已经说到了 【页面模板解析成 VNode 树】的步骤spa
那今天就就到了 【页面VNode生成DOM挂载】 了prototype
等等,今天说的不是 Component 挂载DOM 吗?跟页面Vnode 有什么关系??是啊,component 的挂载确定是跟着父页面的啊,你本身挂?自挂东南枝吗?3d
好了,废话不说,立刻开始
这篇 从模板到DOM的简要流程 已经说过下面的步骤
1vm._render 执行获得 页面VNode
2vm._update 拿到 页面VNode ,会开始 patch,不断比对 【旧VNode 和 刚拿到的新VNode】
对比完以后,会调用一个 createElm 的方法去建立DOM,而后插入页面
那如今,咱们就从 createElm 这个方法突破,前面的流程跟本内容无关,一概略过
function createElm(vnode, parentElm, refElm) {
// 组件须要特殊处理
if (createComponent(vnode, parentElm, refElm)) return
...正常的标签,须要不断递归子节点调用 createElm ,
而后生成DOM,并插入到父节点
}
复制代码
createElm 的做用就是根据 标签名建立 DOM 节点,而后挂载到父节点中,其中参数以下
parentElm == 父DOM 节点
refElm == 兄弟DOM节点,你插入父节点,可能也要知道插在谁附近不是吗,不能乱插的
复制代码
而后很明显,createElm 每次掉要给你都会调用 【createComponent】 去检测这个标签是不是组件
若是是组件,就会去建立这个组件的实例,而且 返回 true,从而不用去执行 createElm 下面的部分
看下 createComponent
function createComponent(vnode, parentElm, refElm) {
var data = vnode.data;
var hook = i.hook;
var init = i.init;
// 调用子组件的 init 方法, init 方法就是 Vue.prototype._init
if (init) {
// 建立子组件的 vm 实例
init(vnode, parentElm, refElm);
// 若是存在组件实例,就是上一步建立成功了
if (vnode.componentInstance) {
return true
}
}
}
复制代码
有没有好奇 vnode.data.hook.init 是什么吗?
他是每一个组件,都会被 【注册进外壳节点的钩子函数】,没错,就是下面的钩子,源码
没错,这就是那个钩子的源码
var componentVNodeHooks = {
init(vnode, parentElm, refElm) {
var vm=
vnode.componentInstance =
createComponentInstanceForVnode(
vnode,activeInstance,
parentElm, refElm
);
// 由于 在 Vue.prototype._init 中 ,只有 $options存在 el,才会挂载 dom
// 这里手动挂载组件
vm.$mount(vnode.elm);
}
...
}
复制代码
那么,钩子是何时注册的呢?
嗯,在上一篇文章,【建立组件外壳VNode的过程当中】,而后保存到了外壳节点的 data 上
function createComponent(
Ctor, data, context,
children, tag
) {
...建立组件构造函数
var hooks = data.hook || (data.hook = {});
data.hook.init = componentVNodeHooks.init
...建立组件VNode,并保存组件构造函数 和钩子 等到 vnode 中
}
复制代码
打印一下实际VNode,没错,有不少钩子,可是如今只说 init
来吧,仔细看那个init 钩子源码,你能够看到调用了一个方法
createComponentInstanceForVnode
开始深刻探索它.........
createComponentInstanceForVnode 函数做用就是给 component 【增长定制options】 + 【调用组件构造函数】
function createComponentInstanceForVnode(
vnode, parent,
parentElm, refElm
) {
// 增长 component 特有options
var options = {
_isComponent: true,
parent: parent, // 父实例
_parentVnode: vnode, // 外壳节点
_parentElm: parentElm , // 父DOM
_refElm: refElm // 兄弟DOM
};
// vnode.components.Ctor 就是 构造函数 ,里面会调用 Vue.prototype._init
return new vnode.componentOptions.Ctor(options)
}
复制代码
vnode.componentOptions.Ctor 就是 构造函数,就是下面这个,上篇文章 Component - 建立组件VNode 时保存在外壳节点的
function VueComponent(options) {
this._init(options);
}
复制代码
new 了以后,天然而然,走到了 _init 方法,在 init 方法中,有一个特殊照顾 component 的方法,专门给 component 实例设置options
"这一步跟 挂载组件DOM 没什么关联,想去掉的,可是想一想仍是先保留下来,完整整个流程"
Vue.prototype._init = function(options) {
if (若是是组件) {
initInternalComponent(vm, options);
}
}
复制代码
组件初始化 initInteralComponent
function initInternalComponent(vm, options) {
// 这个options 就是在建立构造函数时,合并的 options,全局选项和组件设置选项
var opts = vm.$options = Object.create(vm.constructor.options);
// 保存父节点,外壳节点,兄弟节点等
var parentVnode = options._parentVnode; // _parentVnode 是外壳节点
opts.parent = options.parent; // options.parent 是 父实例
opts._parentVnode = parentVnode;
opts._parentElm = options._parentElm;
opts._refElm = options._refElm;
// 保存父组件给子组件关联的数据
var vnodeComponentOptions = parentVnode.componentOptions;
opts.propsData = vnodeComponentOptions.propsData;
opts._parentListeners = vnodeComponentOptions.listeners;
opts._renderChildren = vnodeComponentOptions.children;
opts._componentTag = vnodeComponentOptions.tag;
// 保存渲染函数
if (options.render) {
opts.render = options.render;
opts.staticRenderFns = options.staticRenderFns;
}
}
复制代码
这个时候, init 的过程就完成了
下一步就是到了 mount 过程
能够再回看下 「componentVNodeHooks.init 」 那个钩子源码
在建立组件实例成功以后,会手动调用实例 vm.$mount 进行挂载,就是这句代码完成的功能
然而,挂载的步骤,就是正常标签挂载的步骤了
详情能够查看 从模板到DOM的简要流程 的 mount 过程,是一毛同样的,就很少说了
一、父页面已经拿到了 VNode,其中会调用 createElm 根据 VNode 生成DOM,进行挂载
二、不断的递归遍历子节点,使用 createComponent 判断标签是不是组件
三、遇到组件,拿到组件外壳VNode 的data(data 保存有父组件给子组件的,事件,props,构造函数,钩子)
四、从 data 中拿到 hook,hook 中拿到 init 钩子,并执行 init 钩子
五、init 钩子中,调用 createComponentInstanceForVnode 调用组件构造函数,并返回组件
六、init 钩子中,使用上一步返回的实例,手动调用 vm.$mount 进行组件内部模板解析渲染,并挂载