vue-core 3.3alpha版本源码解析2--挂载应用(mount)

承接上篇文章:vue-core 3.3alpha版本源码解析1--创建应用_zhang78529的博客-CSDN博客 

创建应用后需要挂载到指定的dom元素上,现在开始对挂载源码的源码进行解析

上篇文章中有叙述runtime-dom中所导出的createApp函数,对mount进行了装饰,这里不在重复叙述。

mount核心部分实现于createAppAPI所返回的方法中创建的app对象的mount方法

mount(
        rootContainer: HostElement,
        isHydrate?: boolean,
        isSVG?: boolean
      ): any {
        if (!isMounted) {//未挂载情况下开始挂载
          
          if (__DEV__ && (rootContainer as any).__vue_app__) {
            warn(
              `There is already an app instance mounted on the host container.\n` +
                ` If you want to mount another app on the same host container,` +
                ` you need to unmount the previous app by calling \`app.unmount()\` first.`
            )
          }
          //创建根虚拟节点
          const vnode = createVNode(
            rootComponent as ConcreteComponent,
            rootProps
          )

          //根虚拟节点的appContext指向上下文
          vnode.appContext = context

          // HMR root reload
          if (__DEV__) {//开发模式下,定义reload方法,render克隆的vnode
            context.reload = () => {
              render(cloneVNode(vnode), rootContainer, isSVG)
            }
          }

          if (isHydrate && hydrate) {
            hydrate(vnode as VNode<Node, Element>, rootContainer as any)
          } else {
            //调用baseCreateRenderer中render函数进行渲染
            render(vnode, rootContainer, isSVG)
          }
          //渲染后,状态变成已挂载
          isMounted = true
          //app的_container指向根节点容器
          app._container = rootContainer
          //根节点容器的__vue_app__属性指向app
          ;(rootContainer as any).__vue_app__ = app

          if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
            app._instance = vnode.component
            devtoolsInitApp(app, version)
          }
         
          return getExposeProxy(vnode.component!) || vnode.component!.proxy
        } else if (__DEV__) {
          warn(
            `App has already been mounted.\n` +
              `If you want to remount the same app, move your app creation logic ` +
              `into a factory function and create fresh app instances for each ` +
              `mount - e.g. \`const createMyApp = () => createApp(App)\``
          )
        }
      },

从宏观角度看上述代码,挂载时首先创建根虚拟节点,参数rootComponent,rootProps是导出createApp函数传入的第一、二个参数,分别为根组件模板和根组件属性。然后根据根虚拟节点调用baseCreateRenderer中render函数进行渲染,渲染完成后改变isMounted状态为已挂载,app的_container属性指向容器元素。

因为render函数是基于传入vnode根虚拟节点渲染的,所以先深入解析createVNode函数所创建的vnode

export const createVNode = (
  __DEV__ ? createVNodeWithArgsTransform : _createVNode
) as typeof _createVNode

createVNode是一个包装函数,根据__DEV__的值,执行不同的函数,但是

const createVNodeWithArgsTransform = (
  ...args: Parameters<typeof _createVNode>
): VNode => {
  return _createVNode(
    ...(vnodeArgsTransformer
      ? vnodeArgsTransformer(args, currentRenderingInstance)
      : args)
  )
}

createVNodeWithArgsTransform本质上还是_createVNode函数的封装,只是添加了对参数的处理

因此此处核心是调用_createVNode函数

function _createVNode(
  type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
  props: (Data & VNodeProps) | null = null,
  children: unknown = null,
  patchFlag: number = 0,
  dynamicProps: string[] | null = null,
  isBlockNode = false
): VNode {
  //不存在type时,type定义为注释
  if (!type || type === NULL_DYNAMIC_COMPONENT) {
    if (__DEV__ && !type) {
      warn(`Invalid vnode type when creating vnode: ${type}.`)
    }
    type = Comment
  }

  //使用已存在的vnode创建新虚拟节点,这种一般发生在<component :is="vnode"/>情况下,暂时跳过
  if (isVNode(type)) {
    const cloned = cloneVNode(type, props, true /* mergeRef: true */)
    if (children) {
      normalizeChildren(cloned, children)
    }
    if (isBlockTreeEnabled > 0 && !isBlockNode && currentBlock) {
      if (cloned.shapeFlag & ShapeFlags.COMPONENT) {
        currentBlock[currentBlock.indexOf(type)] = cloned
      } else {
        currentBlock.push(cloned)
      }
    }
    cloned.patchFlag |= PatchFlags.BAIL
    return cloned
  }

  // 如果type是class组件,则将__vccOpts赋值给type
  if (isClassComponent(type)) {
    type = type.__vccOpts
  }

  // 兼容性处理,可跳过
  if (__COMPAT__) {
    type = convertLegacyComponent(type, currentRenderingInstance)
  }

  // 对class和style进行规范化处理
  if (props) {
    // 对响应式对象或者代理对象,进行克隆后返回,非响应式对象直接使用
    props = guardReactiveProps(props)!
    let { class: klass, style } = props
    if (klass && !isString(klass)) {
      //非字符串形式的class属性,使用normalizeClass函数处理成字符串
      props.class = normalizeClass(klass)
    }
    if (isObject(style)) {
      //对象类型的style属性,使用normalizeStyle处理
      // 非数组形式的响应式对象进行Object.assign浅复制处理
      if (isProxy(style) && !isArray(style)) {
        style = extend({}, style)
      }
      props.style = normalizeStyle(style)
    }
  }

  // 根据type的类型判断节点类型
  const shapeFlag = isString(type)
    ? ShapeFlags.ELEMENT
    : __FEATURE_SUSPENSE__ && isSuspense(type)
    ? ShapeFlags.SUSPENSE
    : isTeleport(type)
    ? ShapeFlags.TELEPORT
    : isObject(type)
    ? ShapeFlags.STATEFUL_COMPONENT
    : isFunction(type)
    ? ShapeFlags.FUNCTIONAL_COMPONENT
    : 0

  if (__DEV__ && shapeFlag & ShapeFlags.STATEFUL_COMPONENT && isProxy(type)) {
    type = toRaw(type)
    warn(
      `Vue received a Component which was made a reactive object. This can ` +
        `lead to unnecessary performance overhead, and should be avoided by ` +
        `marking the component with \`markRaw\` or using \`shallowRef\` ` +
        `instead of \`ref\`.`,
      `\nComponent that was made reactive: `,
      type
    )
  }

  return createBaseVNode(
    type,
    props,
    children,
    patchFlag,
    dynamicProps,
    shapeFlag,
    isBlockNode,
    true
  )
}

对上述代码进行总结:_createVNode函数是在调用createBaseVNode函数创建真正的vnode对象前,对传入createBaseVNode的参数进行规范化统一处理。

接下来来看下真正创建vnode对象并返回的createBaseVNode函数。

function createBaseVNode(
  type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
  props: (Data & VNodeProps) | null = null,
  children: unknown = null,
  patchFlag = 0,
  dynamicProps: string[] | null = null,
  shapeFlag = type === Fragment ? 0 : ShapeFlags.ELEMENT,
  isBlockNode = false,
  needFullChildrenNormalization = false
) {
  //创建vnode对象基本属性
  const vnode = {
    __v_isVNode: true,
    __v_skip: true,
    type,
    props,
    key: props && normalizeKey(props),
    ref: props && normalizeRef(props),
    scopeId: currentScopeId,
    slotScopeIds: null,
    children,
    component: null,
    suspense: null,
    ssContent: null,
    ssFallback: null,
    dirs: null,
    transition: null,
    el: null,
    anchor: null,
    target: null,
    targetAnchor: null,
    staticCount: 0,
    shapeFlag,
    patchFlag,
    dynamicProps,
    dynamicChildren: null,
    appContext: null,
    ctx: currentRenderingInstance
  } as VNode
  //如果参数needFullChildrenNormalization为true 则基于vnode对象和children参数进行规范化转换
  if (needFullChildrenNormalization) {
    normalizeChildren(vnode, children)
    // 针对suspense组件的处理,可先跳过
    if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
      ;(type as typeof SuspenseImpl).normalize(vnode)
    }
  } else if (children) {//如果存在children,则根据children类型更改vnode的shapeFlag值
    vnode.shapeFlag |= isString(children)
      ? ShapeFlags.TEXT_CHILDREN
      : ShapeFlags.ARRAY_CHILDREN
  }

  //开发模式下,对key为NaN的情况进行警告提醒
  if (__DEV__ && vnode.key !== vnode.key) {
    warn(`VNode created with invalid key (NaN). VNode type:`, vnode.type)
  }

  
  // 跟踪块级节点树
  if (
    isBlockTreeEnabled > 0 &&
    // 非块级节点和避免跟踪自身
    !isBlockNode &&
    // 存在父节点
    currentBlock &&
    // 除 HOISTED 、BAIL和HYDRATE_EVENTS三种类型patchFlag,其他都需要跟踪
    // 组件类型虚拟节点也需要跟踪
    (vnode.patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
    // the EVENTS flag is only for hydration and if it is the only flag, the
    // vnode should not be considered dynamic due to handler caching.
    vnode.patchFlag !== PatchFlags.HYDRATE_EVENTS
  ) {
    currentBlock.push(vnode)
  }

  //兼容模式
  if (__COMPAT__) {
    convertLegacyVModelProps(vnode)
    defineLegacyVNodeProperties(vnode)
  }

  //返回虚拟节点
  return vnode
}

参考上述代码注释,可以了解到createBaseVNode函数,首先创建vnode对象,并赋予部分属性初始值,并对children规范化处理,最后对满足条件的块级虚拟节点推入currentBlock数组,进行持续跟踪。截止到此处,根虚拟节点实际上只进行了创建和初始化,可以推断出 根虚拟节点的其他属性赋值是在后续的render中完善的。

现在便来看下render吧

const render: RootRenderFunction = (vnode, container, isSVG) => {
    if (vnode == null) {//无虚拟节点情况下,根据container上的_vnode进行卸载
      if (container._vnode) {
        unmount(container._vnode, null, null, true)
      }
    } else {//有虚拟节点情况下,执行patch
      patch(container._vnode || null, vnode, container, null, null, null, isSVG)
    }
    //flushPreFlushCbs函数是effect副作用执行(也就是update周期)前,执行定义的回调函数 watchEffect等
    flushPreFlushCbs()
    //flushPostFlushCbs函数是effect副作用执行(也就是update周期)后,执行定义的回调函数 watchPostEffect等
    flushPostFlushCbs()
    //缓存已渲染的虚拟节点
    container._vnode = vnode
  }

render中在无vnode情况下卸载缓存节点元素,存在vnode时,结合新旧vnode进行patch,flushPreFlushCbs,flushPostFlushCbs主要和后续的update  effect相关,当前先不叙述,后续结合响应式部分一起讲解,patch后端vnode缓存并绑定到到容器的_vnode属性上,作为旧节点数据。

接下来就是众所周知diff算法部分patch

const patch: PatchFn = (
    n1,
    n2,
    container,
    anchor = null,
    parentComponent = null,
    parentSuspense = null,
    isSVG = false,
    slotScopeIds = null,
    optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
  ) => {
    //如果n1和n2两个虚拟节点相同,直接返回
    if (n1 === n2) {
      return
    }
    // 如果新旧节点类型不同,则直接卸载旧节点
    if (n1 && !isSameVNodeType(n1, n2)) {
      anchor = getNextHostNode(n1)
      unmount(n1, parentComponent, parentSuspense, true)
      n1 = null
    }

    //如果是BAIL类型的静态节点标记,不执行优化模式
    if (n2.patchFlag === PatchFlags.BAIL) {
      optimized = false
      n2.dynamicChildren = null
    }

    const { type, ref, shapeFlag } = n2
    //结合type和shapeFlag判断需要执行的对应process函数
    switch (type) {
      case Text:
        processText(n1, n2, container, anchor)
        break
      case Comment:
        processCommentNode(n1, n2, container, anchor)
        break
      case Static:
        if (n1 == null) {
          mountStaticNode(n2, container, anchor, isSVG)
        } else if (__DEV__) {
          patchStaticNode(n1, n2, container, isSVG)
        }
        break
      case Fragment:
        processFragment(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
        break
      default:
        if (shapeFlag & ShapeFlags.ELEMENT) {
          processElement(
            n1,
            n2,
            container,
            anchor,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds,
            optimized
          )
        } else if (shapeFlag & ShapeFlags.COMPONENT) {
          processComponent(
            n1,
            n2,
            container,
            anchor,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds,
            optimized
          )
        } else if (shapeFlag & ShapeFlags.TELEPORT) {
          ;(type as typeof TeleportImpl).process(
            n1 as TeleportVNode,
            n2 as TeleportVNode,
            container,
            anchor,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds,
            optimized,
            internals
          )
        } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
          ;(type as typeof SuspenseImpl).process(
            n1,
            n2,
            container,
            anchor,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds,
            optimized,
            internals
          )
        } else if (__DEV__) {
          warn('Invalid VNode type:', type, `(${typeof type})`)
        }
    }

    //组件的ref属性,根vnode是不存在parentComponent的,可先跳过
    if (ref != null && parentComponent) {
      setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
    }
  }

patch函数对照新旧虚拟节点,进行一些判断,标记,识别新节点的type和shapeFlag进行任务分发,执行不同的process函数,首次mount的是渲染根虚拟节点组件,所以对应执行的是processComponent函数

const processComponent = (
    n1: VNode | null,
    n2: VNode,
    container: RendererElement,
    anchor: RendererNode | null,
    parentComponent: ComponentInternalInstance | null,
    parentSuspense: SuspenseBoundary | null,
    isSVG: boolean,
    slotScopeIds: string[] | null,
    optimized: boolean
  ) => {
    //绑定slot插槽名称数组
    n2.slotScopeIds = slotScopeIds
    if (n1 == null) {//无旧虚拟节点,为首次加载或者激活keep alive组件
      //keep alive组件,通过父组件激活,先跳过
      if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
        ;(parentComponent!.ctx as KeepAliveContext).activate(
          n2,
          container,
          anchor,
          isSVG,
          optimized
        )
      } else {
        //挂载组件
        mountComponent(
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          optimized
        )
      }
    } else {
      //同时存在新旧节点时,更新组件
      updateComponent(n1, n2, optimized)
    }
  }

processComponent函数用来区分keep alive激活 、首次加载及更新组件的情况,并调用不同函数实现激活、加载或更新。下一步需要使用mountComponent进行首次挂载。

const mountComponent: MountComponentFn = (
    initialVNode,
    container,
    anchor,
    parentComponent,
    parentSuspense,
    isSVG,
    optimized
  ) => {
    //兼容性处理,先跳过
    const compatMountInstance =
      __COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
    //存在上一步兼容实例时,直接使用
    //创建组件实例并绑定为虚拟节点的component
    const instance: ComponentInternalInstance =
      compatMountInstance ||
      (initialVNode.component = createComponentInstance(
        initialVNode,
        parentComponent,
        parentSuspense
      ))

    //热更新情况下的特殊处理,暂时跳过
    if (__DEV__ && instance.type.__hmrId) {
      registerHMR(instance)
    }

    //开发模式下的特殊处理,暂时跳过
    if (__DEV__) {
      pushWarningContext(initialVNode)
      //startMeasure是基于window.performance实现的主要是配合endMeasure记录对应操作的耗时
      startMeasure(instance, `mount`)
    }

    //如果是KeepAlive虚拟节点,则将一些函数打包成对象赋值给实例的上下文ctx的renderer属性
    if (isKeepAlive(initialVNode)) {
      ;(instance.ctx as KeepAliveContext).renderer = internals
    }

    // 在非兼容模式下,执行component的setup
    if (!(__COMPAT__ && compatMountInstance)) {
      if (__DEV__) {
        startMeasure(instance, `init`)
      }
      //主要针对组件的setup做处理与instance绑定,生成instance的render方法
      setupComponent(instance)
      if (__DEV__) {
        endMeasure(instance, `init`)
      }
    }

    //此处是针对SUSPENSE组件异步的setup处理,暂时先跳过
    if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
      parentSuspense && parentSuspense.registerDep(instance, setupRenderEffect)

      // Give it a placeholder if this is not hydration
      // TODO handle self-defined fallback
      if (!initialVNode.el) {
        const placeholder = (instance.subTree = createVNode(Comment))
        processCommentNode(null, placeholder, container!, anchor)
      }
      return
    }
    //setupComponent后,基于instance执行instance.render和执行effect等
    setupRenderEffect(
      instance,
      initialVNode,
      container,
      anchor,
      parentSuspense,
      isSVG,
      optimized
    )

    if (__DEV__) {
      popWarningContext()
      endMeasure(instance, `mount`)
    }
  }

mountComponent函数首先生成组件实例instance,随后执行自定义模板的setup,初始化属性、插槽、定义instance.render等,最后调用setupRenderEffect函数,根据setupComponent的结果,执行实例的render和effect等。

接下来先深入setupComponent看看它做了什么

export function setupComponent(
  instance: ComponentInternalInstance,
  isSSR = false
) {
  isInSSRComponentSetup = isSSR

  const { props, children } = instance.vnode
  //判断是否状态类型的组件,shapeFlag类型的判断赋值在_createVNode中
  const isStateful = isStatefulComponent(instance)
  //初始化属性
  initProps(instance, props, isStateful, isSSR)
  //初始化插槽
  initSlots(instance, children)

  //状态组件,需要执行模板定义的setup方法,并取得结果
  const setupResult = isStateful
    ? setupStatefulComponent(instance, isSSR)
    : undefined
  isInSSRComponentSetup = false
  return setupResult
}
function setupStatefulComponent(
  instance: ComponentInternalInstance,
  isSSR: boolean
) {
  //Component和instance.type皆指向createApp传入的第一个模板对象
  const Component = instance.type as ComponentOptions

  if (__DEV__) {
    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])
      }
    }
    if (Component.compilerOptions && isRuntimeOnly()) {
      warn(
        `"compilerOptions" is only supported when using a build of Vue that ` +
          `includes the runtime compiler. Since you are using a runtime-only ` +
          `build, the options should be passed via your build tool config instead.`
      )
    }
  }
  // accessCache先设置成空对象
  instance.accessCache = Object.create(null)
  //将ctx上下文处理成代理对象,并设置SKIP为true,赋值给proxy
  instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers))
  //开发模式,先跳过
  if (__DEV__) {
    exposePropsOnRenderContext(instance)
  }
  
  const { setup } = Component
  //如果定义的模板中有setup方法,则执行setup后再执行finishComponentSetup,否则直接执行finishComponentSetup
  if (setup) {
    //如果setup是数组形式,则使用createSetupContext创建setupContext,否则setupContext为null
    const setupContext = (instance.setupContext =
      setup.length > 1 ? createSetupContext(instance) : null)
    //设置全局激活组件实例为该instance
    setCurrentInstance(instance)
    //暂停调度器跟踪
    pauseTracking()
    //callWithErrorHandling是方法执行异常捕获函数,主要作用是执行第一次参数函数执行异常捕获并抛出警告
    //本质是是执行setup方法,setupResult是setup方法return的值
    const setupResult = callWithErrorHandling(
      setup,
      instance,
      ErrorCodes.SETUP_FUNCTION,
      [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
    )
    //setup方法执行完后重新开启调度器跟踪
    resetTracking()
    //置空全局激活组件实例
    unsetCurrentInstance()

    if (isPromise(setupResult)) {//setup返回异步函数,暂时跳过
      setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
      if (isSSR) {
        // return the promise so server-renderer can wait on it
        return setupResult
          .then((resolvedResult: unknown) => {
            handleSetupResult(instance, resolvedResult, isSSR)
          })
          .catch(e => {
            handleError(e, instance, ErrorCodes.SETUP_FUNCTION)
          })
      } else if (__FEATURE_SUSPENSE__) {
        // async setup returned Promise.
        // bail here and wait for re-entry.
        instance.asyncDep = setupResult
        if (__DEV__ && !instance.suspense) {
          const name = Component.name ?? 'Anonymous'
          warn(
            `Component <${name}>: setup function returned a promise, but no ` +
              `<Suspense> boundary was found in the parent component tree. ` +
              `A component with async setup() must be nested in a <Suspense> ` +
              `in order to be rendered.`
          )
        }
      } else if (__DEV__) {
        warn(
          `setup() returned a Promise, but the version of Vue you are using ` +
            `does not support it yet.`
        )
      }
    } else {
      //非异步函数,执行handleSetupResult
      handleSetupResult(instance, setupResult, isSSR)
    }
  } else {
    finishComponentSetup(instance, isSSR)
  }
}
export function handleSetupResult(
  instance: ComponentInternalInstance,
  setupResult: unknown,
  isSSR: boolean
) {
  
  if (isFunction(setupResult)) {
    //setup中返回的是函数,则直接赋值,ssr赋值给ssrRender,非ssr赋值给render
    if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) {
      // when the function's name is `ssrRender` (compiled by SFC inline mode),
      // set it as ssrRender instead.
      instance.ssrRender = setupResult
    } else {
      instance.render = setupResult as InternalRenderFunction
    }
  } else if (isObject(setupResult)) {
    //开发模式,先跳过
    if (__DEV__ && isVNode(setupResult)) {
      warn(
        `setup() should not return VNodes directly - ` +
          `return a render function instead.`
      )
    }
     //开发模式,先跳过
    if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
      instance.devtoolsRawSetupState = setupResult
    }
    //setup中返回的是对象,则使用proxy包装后赋值给setupState
    instance.setupState = proxyRefs(setupResult)
    if (__DEV__) {
      exposeSetupStateOnRenderContext(instance)
    }
  } else if (__DEV__ && setupResult !== undefined) {
    warn(
      `setup() should return an object. Received: ${
        setupResult === null ? 'null' : typeof setupResult
      }`
    )
  }
  finishComponentSetup(instance, isSSR)
}
export function finishComponentSetup(
  instance: ComponentInternalInstance,
  isSSR: boolean,
  skipOptions?: boolean
) {
  //Component和instance.type皆指向createApp传入的第一个模板对象
  const Component = instance.type as ComponentOptions

  //兼容模式处理,先跳过
  if (__COMPAT__) {
    convertLegacyRenderFn(instance)

    if (__DEV__ && Component.compatConfig) {
      validateCompatConfig(Component.compatConfig)
    }
  }

  //setup返回为对象时,instance的render为null
  if (!instance.render) {
    //非ssr模式执行
    if (!isSSR && compile && !Component.render) {
      //非兼容模式下优先取自定义模板的template,否则取实例的template
      const template =
        (__COMPAT__ &&
          instance.vnode.props &&
          instance.vnode.props['inline-template']) ||
        Component.template ||
        resolveMergedOptions(instance).template
      if (template) {
        //开发模式下,开始解析模板计时
        if (__DEV__) {
          startMeasure(instance, `compile`)
        }
        //取全局配置和组件配置,并合并
        const { isCustomElement, compilerOptions } = instance.appContext.config
        const { delimiters, compilerOptions: componentCompilerOptions } =
          Component
        const finalCompilerOptions: CompilerOptions = extend(
          extend(
            {
              isCustomElement,
              delimiters
            },
              
          ),
          componentCompilerOptions
        )
        //兼容模式  先跳过
        if (__COMPAT__) {
          
          finalCompilerOptions.compatConfig = Object.create(globalCompatConfig)
          if (Component.compatConfig) {
            // @ts-expect-error types are not compatible
            extend(finalCompilerOptions.compatConfig, Component.compatConfig)
          }
        }
        //调用解析器生成render方法,并赋值给Component.render
        Component.render = compile(template, finalCompilerOptions)
        if (__DEV__) {
          endMeasure(instance, `compile`)
        }
      }
    }
    //解析器生成render方法同时赋值给instance.render
    instance.render = (Component.render || NOOP) as InternalRenderFunction


   //installWithProxy函数在registerRuntimeCompiler时设置
   //registerRuntimeCompiler中的installWithProxy主要作用是当instance.render的_rc为true时创建instance.ctx的代理对象赋值给instance.withProxy属性
    if (installWithProxy) {
      installWithProxy(instance)
    }
  }

  
  // 兼容处理,先跳过
  if (__FEATURE_OPTIONS_API__ && !(__COMPAT__ && skipOptions)) {
    setCurrentInstance(instance)
    pauseTracking()
    applyOptions(instance)
    resetTracking()
    unsetCurrentInstance()
  }

  //开发模式下,无render警告
  if (__DEV__ && !Component.render && instance.render === NOOP && !isSSR) {
    /* istanbul ignore if */
    if (!compile && Component.template) {
      warn(
        `Component provided template option but ` +
          `runtime compilation is not supported in this build of Vue.` +
          (__ESM_BUNDLER__
            ? ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".`
            : __ESM_BROWSER__
            ? ` Use "vue.esm-browser.js" instead.`
            : __GLOBAL__
            ? ` Use "vue.global.js" instead.`
            : ``) /* should not happen */
      )
    } else {
      warn(`Component is missing template or render function.`)
    }
  }
}

setupComponent执行流程setupComponent==>setupStatefulComponent==>handleSetupResult==>finishComponentSetup,参考上述注释可以发现setupComponent主要是执行模板的setup完善组件实例instance。

最后来看看setupRenderEffect

const setupRenderEffect: SetupRenderEffectFn = (
    instance,
    initialVNode,
    container,
    anchor,
    parentSuspense,
    isSVG,
    optimized
  ) => {
    //组件更新函数
    const componentUpdateFn = () => {
      if (!instance.isMounted) {
        let vnodeHook: VNodeHook | null | undefined
        const { el, props } = initialVNode
        const { bm, m, parent } = instance
        const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)

        //禁用scheduler调度器递归执行
        toggleRecurse(instance, false)
        //执行自定义的beforeMount周期回调
        if (bm) {
          invokeArrayFns(bm)
        }
        // 执行自定义的onVnodeBeforeMount周期回调
        if (
          !isAsyncWrapperVNode &&
          (vnodeHook = props && props.onVnodeBeforeMount)
        ) {
          
          invokeVNodeHook(vnodeHook, parent, initialVNode)
        }
        //兼容模式  先跳过
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          instance.emit('hook:beforeMount')
        }
        //开启scheduler调度器递归执行
        toggleRecurse(instance, true)

        if (el && hydrateNode) {//hydrate模式下的处理,暂时跳过
          const hydrateSubTree = () => {
            if (__DEV__) {
              startMeasure(instance, `render`)
            }
            instance.subTree = renderComponentRoot(instance)
            if (__DEV__) {
              endMeasure(instance, `render`)
            }
            if (__DEV__) {
              startMeasure(instance, `hydrate`)
            }
            hydrateNode!(
              el as Node,
              instance.subTree,
              instance,
              parentSuspense,
              null
            )
            if (__DEV__) {
              endMeasure(instance, `hydrate`)
            }
          }

          if (isAsyncWrapperVNode) {
            ;(initialVNode.type as ComponentOptions).__asyncLoader!().then(
              // note: we are moving the render call into an async callback,
              // which means it won't track dependencies - but it's ok because
              // a server-rendered async wrapper is already in resolved state
              // and it will never need to change.
              () => !instance.isUnmounted && hydrateSubTree()
            )
          } else {
            hydrateSubTree()
          }
        } else {
          //开发模式下,开始测量render计时
          if (__DEV__) {
            startMeasure(instance, `render`)
          }
          //
          const subTree = (instance.subTree = renderComponentRoot(instance))
          if (__DEV__) {
            endMeasure(instance, `render`)
          }
          //开发模式下,开始测量子节点patch计时
          if (__DEV__) {
            startMeasure(instance, `patch`)
          }
          //根据subTree数据进行patch,
          patch(
            null,
            subTree,
            container,
            anchor,
            instance,
            parentSuspense,
            isSVG
          )
          
          if (__DEV__) {
            endMeasure(instance, `patch`)
          }
          initialVNode.el = subTree.el
        }
        // 将自定义的mounted的周期回调推入effect副作用队列执行完后的队列,并按队列顺序开始执行
        if (m) {
          queuePostRenderEffect(m, parentSuspense)
        }
        // 将自定义的onVnodeMounted的周期回调推入effect副作用队列执行完后的队列,并按队列顺序开始执行
        if (
          !isAsyncWrapperVNode &&
          (vnodeHook = props && props.onVnodeMounted)
        ) {
          const scopedInitialVNode = initialVNode
          queuePostRenderEffect(
            () => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
            parentSuspense
          )
        }
        //兼容模式处理,暂时跳过
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          queuePostRenderEffect(
            () => instance.emit('hook:mounted'),
            parentSuspense
          )
        }

        //keep-alive组件的处理,暂时跳过
        if (
          initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ||
          (parent &&
            isAsyncWrapper(parent.vnode) &&
            parent.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE)
        ) {
          instance.a && queuePostRenderEffect(instance.a, parentSuspense)
          if (
            __COMPAT__ &&
            isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
          ) {
            queuePostRenderEffect(
              () => instance.emit('hook:activated'),
              parentSuspense
            )
          }
        }
        //标记组件实例已挂载
        instance.isMounted = true

        if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
          devtoolsComponentAdded(instance)
        }
        
        // #2458: deference mount-only object parameters to prevent memleaks
        initialVNode = container = anchor = null as any
      } else {
        // updateComponent
        // This is triggered by mutation of component's own state (next: null)
        // OR parent calling processComponent (next: VNode)
        let { next, bu, u, parent, vnode } = instance
        let originNext = next
        let vnodeHook: VNodeHook | null | undefined
        if (__DEV__) {
          pushWarningContext(next || instance.vnode)
        }

        // Disallow component effect recursion during pre-lifecycle hooks.
        toggleRecurse(instance, false)
        if (next) {
          next.el = vnode.el
          updateComponentPreRender(instance, next, optimized)
        } else {
          next = vnode
        }

        // beforeUpdate hook
        if (bu) {
          invokeArrayFns(bu)
        }
        // onVnodeBeforeUpdate
        if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
          invokeVNodeHook(vnodeHook, parent, next, vnode)
        }
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          instance.emit('hook:beforeUpdate')
        }
        toggleRecurse(instance, true)

        // render
        if (__DEV__) {
          startMeasure(instance, `render`)
        }
        const nextTree = renderComponentRoot(instance)
        if (__DEV__) {
          endMeasure(instance, `render`)
        }
        const prevTree = instance.subTree
        instance.subTree = nextTree

        if (__DEV__) {
          startMeasure(instance, `patch`)
        }
        patch(
          prevTree,
          nextTree,
          // parent may have changed if it's in a teleport
          hostParentNode(prevTree.el!)!,
          // anchor may have changed if it's in a fragment
          getNextHostNode(prevTree),
          instance,
          parentSuspense,
          isSVG
        )
        if (__DEV__) {
          endMeasure(instance, `patch`)
        }
        next.el = nextTree.el
        if (originNext === null) {
          // self-triggered update. In case of HOC, update parent component
          // vnode el. HOC is indicated by parent instance's subTree pointing
          // to child component's vnode
          updateHOCHostEl(instance, nextTree.el)
        }
        // updated hook
        if (u) {
          queuePostRenderEffect(u, parentSuspense)
        }
        // onVnodeUpdated
        if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
          queuePostRenderEffect(
            () => invokeVNodeHook(vnodeHook!, parent, next!, vnode),
            parentSuspense
          )
        }
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          queuePostRenderEffect(
            () => instance.emit('hook:updated'),
            parentSuspense
          )
        }

        if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
          devtoolsComponentUpdated(instance)
        }

        if (__DEV__) {
          popWarningContext()
        }
      }
    }

    // 创建instance响应式effect副作用实例
    const effect = (instance.effect = new ReactiveEffect(
      componentUpdateFn,//副作用执行函数
      () => queueJob(update),//此处可以理解成缓存instance的update,并在响应式trigger的时候,推入执行队列,并执行
      instance.scope // 将该副作用推入到instance.scope.effects数组上
    ))

    //update方法为调用上面effect的run函数
    const update: SchedulerJob = (instance.update = () => effect.run())
    //update的id绑定instance的唯一id,作用于effect执行队列的优先级排序
    update.id = instance.uid
    //允许effect副作用递归执行
    toggleRecurse(instance, true)

    //开发模式特殊处理,先跳过
    if (__DEV__) {
      effect.onTrack = instance.rtc
        ? e => invokeArrayFns(instance.rtc!, e)
        : void 0
      effect.onTrigger = instance.rtg
        ? e => invokeArrayFns(instance.rtg!, e)
        : void 0
      update.ownerInstance = instance
    }
    //先执行一次effect.run
    update()
  }

setupRenderEffect定义了组件更新函数,并创建ReactiveEffect实例并赋值给effect,定义update执行effect.run,最后执行到componentUpdateFn,componentUpdateFn中,首次挂载,首先执行对应生命周期的回调函数,然后根据instance.render生成subTree节点,进行patch渲染最终页面(此处patch不深入解析),完成整个mount流程。

在mount流程中,compile流程生成instance.render及instance.render生成subTree,将在下篇文章进行解析,effect部分将结合响应式部分详解

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值