vue2.x源码解析六——组件化--3.patch(将虚拟DOM映射为真实DOM)

1.patch

通过vue2.x源码解析六——数据驱动,当我们通过 createComponent 创建了组件 VNode,接下来会走到

vm._update —> vm.patch –> patch 方法,

去把 VNode 转换成真正的 DOM 节点。

这个过程我们在前一章已经分析过一个普通的 VNode 节点的path过程,但是针对的是一个普通的 VNode 节点,接下来我们来看看组件的 VNode 会有哪些不一样的地方。

patch 的过程会调用 createElm 创建元素节点实现虚拟DOM映射为真实DOM(要注意区分,render也就是参数虚拟DOM用的是createElement,映射为真实DOM createElm),回顾一下 createElm 的实现,它的定义在 src/core/vdom/patch.js 中:

function createElm (
  vnode,
  insertedVnodeQueue,
  parentElm,
  refElm,
  nested,
  ownerArray,
  index
) {
  // ...
  if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
    return
  }
  // ...
}

2.createComponent

我们删掉多余的代码,只保留关键的逻辑,上面的代码会判断 createComponent(vnode, insertedVnodeQueue, parentElm, refElm) 的返回值,如果为 true 则直接结束。

createComponent其实调用的就是给逐渐VNode添加的init方法

那么接下来看一下 createComponent 方法的实现:

function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
  let i = vnode.data
  if (isDef(i)) {
    const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
    // 判断vnode.data中是否有hook,并且有init方法(因为上一节讲了会给组件merage一些钩子,其中就有init,所以这里是trueif (isDef(i = i.hook) && isDef(i = i.init)) {
      i(vnode, false /* hydrating */)
    }
    // after calling the init hook, if the vnode is a child component
    // it should've created a child instance and mounted it. the child
    // component also has set the placeholder vnode's elm.
    // in that case we can just return the element and be done.
    if (isDef(vnode.componentInstance)) {
      initComponent(vnode, insertedVnodeQueue)
      insert(parentElm, vnode.elm, refElm)
      if (isTrue(isReactivated)) {
        reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
      }
      return true
    }
  }
}

2.1 对vnode.data 做了一些判断

let i = vnode.data
if (isDef(i)) {
  // ...
  if (isDef(i = i.hook) && isDef(i = i.init)) {
    i(vnode, false /* hydrating */)
    // ...
  }
  // ..
}
  1. vnode 是一个组件 VNode,那么条件会满足,并且得到 i 就是 init 钩子函数

  2. 判断vnode.data中是否有hook,并且有init方法(因为上一节讲了会给组件VNode merage一些钩子,其中就有init,所以这里是true),就会调用init方法

2.2 init方法

init方法定义在 src/core/vdom/create-component.js 中:

其实就是和组件的data.hook钩子合并的 componentVNodeHooks 钩子对象的init方法

const componentVNodeHooks = {
       init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
        if (
          vnode.componentInstance &&
          !vnode.componentInstance._isDestroyed &&
          vnode.data.keepAlive
        ) {
          // kept-alive components, treat as a patch
          const mountedNode: any = vnode // work around flow
          componentVNodeHooks.prepatch(mountedNode, mountedNode)
        } else {
            //child是一个vnode实例
          const child = vnode.componentInstance = createComponentInstanceForVnode(
            vnode,
            activeInstance
          )
          //调用 $mount 方法挂载子组件
          child.$mount(hydrating ? vnode.elm : undefined, hydrating)
        }
      },

     prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
     },

     insert (vnode: MountedComponentVNode) {
     },

     destroy (vnode: MountedComponentVNode) {
     }
}

init 钩子函数执行也很简单,我们先不考虑 keepAlive 的情况,它是通过 createComponentInstanceForVnode 创建一个 vnode 的实例,然后调用 $mount 方法挂载子组件

2.3 createComponentInstanceForVnode方法

创建一个 vnode 的实例
src/core/vdom/create-component.js

export function createComponentInstanceForVnode (
  vnode: any, // 组件VNode
  parent: any, // 当前vue实例vm
): Component {
    
    // 定义参数
  const options: InternalComponentOptions = {
    _isComponent: true,
    // 父VNode,是一个占位节点,Vue实例A调用B组件,B调用C组件,_parentVnode就是C组件占位符
    _parentVnode: vnode, 
   //表示当前激活的子组件的父级实例,例如 app =new Vue,并调用子组件,parent就是app
    parent 
  }
     ...

  // 由上一节我们知道组件会生成子构造器,vnode.componentOptions.Ctor 对应的就是子组件的构造函数
  return new vnode.componentOptions.Ctor(options)
}
  1. createComponentInstanceForVnode 函数构造的一个内部组件的参数,然后执行 new
    vnode.componentOptions.Ctor(options)。

  2. 上一节我们知道组件会生成子构造器,vnode.componentOptions.Ctor 对应的就是子组件的构造函数,

  3. 我们上一节分析了子构造器实际上是继承于 Vue 的一个构造器 Sub,相当于 new Sub(options)

  4. 这里有几个关键参数要注意几个点,_isComponent 为 true 表示它是一个组件,parent 表

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值