Skip to content

1 componentVNodeHooks

组件生命周期钩子包含四个关键钩子函数:

ts
const componentVNodeHooks = {
  init(vnode, hydrating) {
    // 处理组件初始化
    // 1. 处理 keep-alive 组件
    // 2. 创建组件实例并挂载
  },
  prepatch(oldVnode, vnode) {
    // 组件更新
  },
  insert(vnode) {
    // 组件插入
  },
  destroy(vnode) {
    // 组件销毁
  }
}

2. createComponent

创建组件的核心方法。将组件元素转换为组件 VNode

譬如:

vue
<my-component />

转换为组件 VNode 后,VNodecomponentOptions 属性中包含组件的构造函数 Ctor

ts
export function createComponent(Ctor, data, context, children, tag) {
  // 1. 组件构造函数检查和处理
  if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor) // 将组件配置对象转为构造函数
  }

  // 2. 异步组件处理
  if (isUndef(Ctor.cid)) {
    asyncFactory = Ctor
    Ctor = resolveAsyncComponent(asyncFactory, baseCtor)
    // 返回注释节点占位符
  }

  // 3. 处理组件数据
  // 处理 v-model
  if (isDef(data.model)) {
    transformModel(Ctor.options, data)
  }
  
  // 4. 提取 props
  const propsData = extractPropsFromVNodeData(data, Ctor, tag)

  // 5. 处理函数式组件
  if (isTrue(Ctor.options.functional)) {
    return createFunctionalComponent(...)
  }

  // 6. 处理事件监听器
  const listeners = data.on
  data.on = data.nativeOn

  // 7. 安装组件钩子函数
  installComponentHooks(data)

  // 8. 创建并返回组件 VNode
  const vnode = new VNode(...)
  return vnode
}

核心功能:

  • 将组件配置转换为构造函数
  • 处理异步组件
  • 处理组件数据(propseventsslots等)
  • 安装组件钩子函数
  • 创建组件 VNode

3. createComponentInstanceForVnode

根据组件 VNode 创建组件实例,也就是转化为 Vue 实例:

ts
function createComponentInstanceForVnode(
  vnode,
  // activeInstance in lifecycle state
  parent
) {
  const options = {
    _isComponent: true,
    _parentVnode: vnode,
    parent
  }
  // check inline-template render functions
  const inlineTemplate = vnode.data.inlineTemplate
  if (isDef(inlineTemplate)) {
    options.render = inlineTemplate.render
    options.staticRenderFns = inlineTemplate.staticRenderFns
  }
  return new vnode.componentOptions.Ctor(options)
}

4. installComponentHooks

安装组件钩子函数:

  • 将组件的生命周期钩子(initprepatchinsertdestroy)安装到 VNode 的 data.hook
  • 处理钩子函数的合并
ts
function installComponentHooks(data) {
  const hooks = data.hook || (data.hook = {})
  for (let i = 0; i < hooksToMerge.length; i++) {
    const key = hooksToMerge[i]
    const existing = hooks[key]
    const toMerge = componentVNodeHooks[key]
    
    if (existing !== toMerge && !(existing && existing._merged)) {
      hooks[key] = existing ? mergeHook(toMerge, existing) : toMerge
    }
  }
}