挂载组件

  1. // 挂载组件
  2. const mountComponent = (
  3. initialVNode,
  4. container,
  5. anchor,
  6. parentComponent,
  7. parentSuspense,
  8. isSVG,
  9. optimized
  10. ) => {
  11. // 1. 创建组件实例
  12. const instance = (initialVNode.component = createComponentInstance(
  13. initialVNode,
  14. parentComponent,
  15. parentSuspense
  16. ))
  17. // inject renderer internals for keepAlive
  18. if (isKeepAlive(initialVNode)) {
  19. instance.ctx.renderer = internals
  20. }
  21. // 2. 设置组件实例
  22. setupComponent(instance)
  23. // setup() is async. This component relies on async logic to be resolved
  24. // before proceeding.
  25. // 3.设置并且运行带有副作用的渲染函数
  26. setupRenderEffect(
  27. instance,
  28. initialVNode,
  29. container,
  30. anchor,
  31. parentSuspense,
  32. isSVG,
  33. optimized
  34. )
  35. }

总结:

  1. 创建一个组件实例 createComponentInstance
  2. 判断是否是 keep-alive
  3. 设置组件实例
  4. 运行 setupRenderEffect

setupComponent

function setupComponent(instance, isSSR = false) {
  const { props, children, shapeFlag } = instance.vnode
  // 是否是状态型组件
  const isStateful = shapeFlag & 4 /* STATEFUL_COMPONENT */
  // 初始化组件属性、插槽
  initProps(instance, props, isStateful, isSSR)
  initSlots(instance, children)
  // 仅为状态型组件挂载setup信息,非状态型组件仅为纯UI展示不需要挂载状态信息
  const setupResult = isStateful
  ? setupStatefulComponent(instance, isSSR)
  : undefined

  return setupResult
}

// 状态型组件生成setup结果并进行信息挂载
function setupStatefulComponent(instance, isSSR) {
  // 组件options Object,也就是vnode.type
  const Component = instance.type
  {
    if (Component.name) {
      // 校验组件名是否合法
      validateComponentName(Component.name, instance.appContext.config)
    }
    if (Component.components) {
      // 批量校验组件名
      const names = Object.keys(Component.components)
      for (let i = 0; i < names.length; i++) {
        validateComponentName(names[i], instance.appContext.config)
      }
    }
    if (Component.directives) {
      // 批量校验指令名是否合法
      const names = Object.keys(Component.directives)
      for (let i = 0; i < names.length; i++) {
        validateDirectiveName(names[i])
      }
    }
  }
  // 0. create render proxy property access cache
  instance.accessCache = Object.create(null)
  // 1. create public instance / render proxy
  // also mark it raw so it's never observed
  // 1. 为组件实例创建渲染代理,同时将代理标记为raw,为的是在后续过程中不会被误转化为
  // 响应式数据,渲染代理源对象是组件实例上下文,代理traps后面会详细讲解
  instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)

  // 2. call setup()
  const { setup } = Component
  if (setup) {
    // 创建setup上下文并挂载到组件实例上
    const setupContext = (instance.setupContext =
                          setup.length > 1 ? createSetupContext(instance) : null)
    // 记录当前正在初始化的组件
    currentInstance = instance
    // 执行setup前暂停依赖收集
    // PS: 执行setup期间不允许进行依赖收集,setup仅为获取我们需要为组件提供的状态信息
    // 不应该在它的里面其他非必要的副作用,真正的依赖收集等有较强副作用的操作应该放到
    // setup挂载之后,以免产生不可预测的问题
    pauseTracking()
    // 执行setup并获得安装结果信息,setup执行结果就是我们定义的响应式数据、函数、钩子等
    const setupResult = callWithErrorHandling(
      setup,
      instance,
      0 /* SETUP_FUNCTION */,
      [shallowReadonly(instance.props), setupContext]
    )
    // setup执行完毕恢复依赖收集
    resetTracking()
    // 当前实例重置
    currentInstance = null
    // setup执行结果挂载
    if (isPromise(setupResult)) {
      if (isSSR) {
        // return the promise so server-renderer can wait on it
        // 在SSR或者suspense时setup返回promise
        // suspense因为有节点fallback,而setup中是正式渲染内容,因此是一个异步resolve的过程
        return setupResult.then((resolvedResult) => {
          handleSetupResult(instance, resolvedResult)
        })
      } else {
        // async setup returned Promise.
        // bail here and wait for re-entry.
        instance.asyncDep = setupResult
      }
    } else {
      // 将setup的执行结果挂载到组件实例上
      handleSetupResult(instance, setupResult)
    }
  } else {
    finishComponentSetup(instance)
  }
}

总结:

  1. 初始化组件的propsslots
  2. setupStatefulComponent方法设置组件状态并挂载
    1. 校验组件名称是否合法
    2. 校验指令是否合法
    3. 为组件实例创建渲染代理
    4. 判断组件是否有 setup 方法,
      1. 有:
        1. 创建 setupContext并赋值给组件实例的setupContext
        2. 记录当前正在初始化的组件
        3. 暂停effect的依赖收集
        4. 执行 setup,并拿到返回的结果 setupResult
        5. 放开 effect 的依赖收集
        6. 将setup执行结果 setupResult 进行 proxy拦截代理,并赋值给 instance.setupState