之前有一篇文章分析了作为一个文本节点,vue是如何进行挂载的。本次通过一个keep-alive的例子带大家来了解vue框架组件的实现原理。例子非常简单,页面长这个样子:
代码如下:
通过switch按钮来使动态组件在组件A和组件B中间切换,其中组件A是一个可以输入的输入框。在A和B中间切换的过程中,通过keep-alive组件可以保留输入的内容。
下面我们通过这个keep-alive组件是如何实现来了解vue的组件机制。以及当我们使用keep-alive的时候,应该注意哪些点。
在引入vue源码的时候,会为vue的原型引入一些原型方法,这些方法留待以后讲解。在new Vue的时候执行了vue这个构造函数,并且执行了_init方法。
可以在右侧看到new Vue时传入的options对象,其中components中包含的A和B组件也都是对象。在_init中对vm这个vue实例做了一系列的初始化以后,快要执行完_init时,通过vm.$mount来把实现把vm这个vue实例挂载到vm.$options.el即”#app”节点上:
由于是在运行时编译(生成render函数)的,上文的mount其实是先要生成render函数vue组件继承,再去调用通用的mount,由于通用的mount函数已经被缓存了,需要在当前实例的作用域下来调用,所以是通过call来调用,第一个参数为this。如下图:
在通用mount中调用mountComponent函数,先执行render函数获取vnode,然后返回vnode:
下面我们来看这里是如何生成vnode的vue组件继承,进入render函数后可以看到编译之后的render函数其实是长这个样子,当然为了便于查看,格式是调整过的:
(function anonymous() {
with(this){
return _c(
'div',
{attrs:{"id":"app"}},
[
_c(
'button',
{on:{"click":change}},
[_v("switch")]
),
_v(" "),
_c(
'keep-alive',
[
_c(
currentComp,
{tag:"component"}
)
],
1
)
],
1
)}
}
)
render函数会先从最里层开始生成vnode,在这里先来生成switch这个文本节点:
再把生成的vnode子节点作为参数传给上层,创建button节点:
在createElement函数中又调用了_createElement函数:
在_createElement中,判断button为保留标签,通过new VNode新建完vnode节点后返回:
接着对换行空白符实例化完了一个文本节点后,就对组件A开始构造vnode节点,调用_c函数:
这次在_createElement函数中,会通过resolveAsset方法,获取components中A组件的options对象,作为Ctor传入createComponent来构造vnode,data为构造vnode时需要的属性结构,context为当前上下文的vue实例:
在createComponent函数中,获取到vue构造函数后,通过extend方法得到组件A的构造函数:
之后在组件的占位节点上安装了钩子,在patch的时候才能体现用处,而下面的new Vnode是真正来实例化出vnode节点的,注意组件vnode中的tag格式比较特殊:
下面对A组件的上一层keepalive继续调用_createElement函数,由于keepalive为内置组件,所以走到了createComponent的逻辑:
可以注意一下children参数为上面构造出来的A组件的vnode:
在对keepalive标签调用createComponent时,还是使组建的构造函数继承了vue的构造函数:
在源码中,我们可以看到继承的逻辑如下,先是以Vue组件实例的原型为原型,赋给子组件初始化函数的原型。再把初始化函数挂到了子组件实例原型的构造函数上,这个逻辑在后面初始化子组件的时候会用到。
在createComponent中,接下来安装钩子和实例化vnode并返回,注意组件vnode下面children属性为undefined,但是componentOptions中有children属性:
下面在render函数中走到了对div节点构造vnode了,由于是保留标签名,在_createElement中直接调用new VNode:
总体执行完render函数,即可得到tag为div的vnode,之后返回vnode:
———END———
限 时 特 惠:本站每日持续更新海量各大内部创业教程,一年会员只需128元,全站资源免费下载点击查看详情
站 长 微 信:jiumai99