new vue 方法参数_通过keepalive实现了解vue组件实现原理(2)

下面带着生成的vnode进入_update函数:

338fee0fc9f9108a8ba63b5696f84f1d.png

接着_update方法的作用是把VNode渲染成真实的DOM,他定义在src/core/instance/lifecycle.js中。_update的核心就是调用vm.__patch__方法,首次渲染时,vm.vnode为空,所以给__patch__传入的vm.$el为真实的#app DOM节点:

643161da5dad5b7dd919f6ab5e4109d7.png

这个方法不同的平台是不一样的,在web平台中它的定义在src/platforms/web/runtime/patch.js文件中,通过createPatchFunction来生成,这里调用createPatchFunction的方法是从core/vdom/patch中引入的,但是此函数的参数是从web平台的文件夹引入的,nodeOps封装了一系列DOM操作的方法,modules定义了一些模块的钩子函数的实现,通过这种方式把核心代码和平台代码进行解耦:

3ea37c9e7452e66564fcf4c4a6613150.png

patch函数中主要调用的函数是createElm函数,注意此处的parentElm是body这个DOM,在最后把vue实例化完DOM节点的时候会挂载上去:

a79f1a32324c439102fcd0420482d8bc.png

在createElm中:

  1. 判断vnode是否包含tag,如果包含,先简单对tag的合法性在非生产环境下做校验,看是否是一个合法标签;然后再去调用平台DOM的操作去创建一个占位符元素vnode.elm。

  2. 调用createChildren方法去创建子元素。遍历子虚拟节点,递归调用createElm,注意在遍历过程中会把vnode.elm作为父容器的DOM节点占位符传入。

  3. 调用invokeCreateHooks方法执行所有的create的钩子并把vnode push到insertedVnodeQueue中。

  4. 最后调用insert方法把DOM插入到父节点中,因为是递归调用,子元素会优先调用insert,所以整个vnode树节点的插入顺序是先子后父。

  5. 最后如果不是标签节点,那么就是注释节点或者文本节点这两种叶子节点,直接生成vnode节点,插入到父DOM元素上。关于节点种类请参考《结合源码理解vue列表渲染v-for中的key属性》

dcf64424cfb4ccb12aed8b86ce9d6e4c.png

上面第4条说是递归调用,其实就是在createChildren函数中,还是会迭代对children调用createElm函数:

bf06df5beba787a664bd856cdd735d1c.png

在对button元素执行createElm时,会通过nodeOps.createElement API来新建DOM节点,插入到父节点parentElm中:

ad9e6dcd9f76270c9b3b8ab87d2b2485.png

但是作为组件的keep-alive却会走到createComponent中,并返回true:

4778679549652428fa0b88e5b4945c78.png

在createComponent中,先来执行组件在生成vnode时componentVNodeHooks混入的init钩子:

ea5228fd5160b466f393430bac31b71b.png

在init钩子中,由于vnode.componentInstance和vnode.data.keepAlive未被定义,所以执行到createComponentInstanceForVnode函数,此处vnode为keepalive组件的vnode:

a4448b5e52e4af3b514c592bb442243a.png

activeInstance为当前的vue实例,通过$options可以看出为根节点挂载的vue实例:

6d54871ca64325f0a00da5bbff53aaa4.png

然后进入到createComponentInstanceForVnode函数中,通过之前继承的构造函数来实例化组件实例然后返回,注意实例化时传入了自定义的options:

d292fb0626591bed6399cc366b9002d8.png

在new语句中,会走到之前组件继承vue初始化函数的这个地方,执行this._init初始化组件实例,而此时的this为VueComponent实例:

75377a2f3b9c549a7463c2a923c1b857.png

在继承的_init方法中,得到_uid为1,传入的options._isComponent为true,所以进入initInternalComponent函数:

540512ff06344f60695b50353b5e9dff.png

在initInternalComponent函数中以vm.constructor.options为原型实例化出的对象赋给vm.$options,就是源码中的keep-alive对象,其中自己定义了render方法:

f9e7a3e0063b29d56d01aeae00efc4c1.png

并在vm.$options中保留传入options中的parent和_parentVnode:

99cac692720d988a3e7fa2eef6cf8c54.png

回到Vue初始化方法原型上的_init,对组件实例各种初始化 ,最后vm.$options.el中并未定义,没有执行_init中的vm.$mount方法,就退出了。

9d52fb473a3e08d1d3e2deb8fddb4a2b.png

回到init钩子中,最后由child调用$mount来实现挂载:

b5ce03cf66dfb50d8fd256c7b607fea2.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值