# 创建流程


const App = {
  template: `
    <div>
      <div>{{state}}</div>
    </div>
  `,
  setup () {
    const state = 'Hello Parent'
    return {
      state
    }
  }
}
createApp(App).mount(document.querySelector('#app'))

我们先从一个例子出发,来看看Vue3.x是如何根据组件生成真实DOM并挂载的。首先创建一个Vue3.x应用需要调用 createApp并传入根组件,再调用mount传入要挂载的DOM节点。


export const createApp = ((...args) => {
  // 首先通过ensureRenderer创建渲染器,这也是Vue3.x支持跨平台的关键
  // 渲染器返回了createApp方法,调用createApp方法创建app实例
  const app = ensureRenderer().createApp(...args)

  if (__DEV__) {
    injectNativeTagCheck(app)
  }
  // 这里先拿到app中定义的mount方法,并重写了mount方法
  const { mount } = app
  // 定义了Web平台的mount方法
  app.mount = (containerOrSelector: Element | string): any => {
    // 如果传入的containerOrSelector是字符串,则通过containerOrSelector转化为DOM节点
    const container = normalizeContainer(containerOrSelector)
    if (!container) return
    // 通过_component拿到保存在app上的根组件定义
    const component = app._component
    // 如果根组件不是函数并且没有定义render函数和template,直接拿到挂载的根节点的innerHTML
    // 赋值给template
    if (!isFunction(component) && !component.render && !component.template) {
      component.template = container.innerHTML
    }
    // 清空container的innerHTML
    container.innerHTML = ''
    // 执行在app中定义的mount方法
    const proxy = mount(container)
    container.removeAttribute('v-cloak')
    container.setAttribute('data-v-app', '')
    return proxy
  }

  return app
}) as CreateAppFunction<Element>


/**
 * ensureRenderer调用并返回了baseCreateRenderer的执行结果
 * 这里baseCreateRenderer为创建Web平台渲染器的方法
 * 在baseCreateRenderer中,options传入的为一系列操作DOM节点的方法
 * 那么在其他平台中,就可以传入不同平台操作节点的方法来创建不同平台的渲染器。
 * 这里createApp是通过createAppAPI创建的,并且传入了定义的render方法
 */

function baseCreateRenderer(options, createHydrationFns) {
  ...
  ...
  const render: RootRenderFunction = (vnode, container) => {
    // 如果传入的vnode是null,则执行的是销毁操作
    // 拿到保存在container上的根组件vnode,调用unmount销毁
    // 否则调用patch执行根组件vnode的patch过程
    if (vnode == null) {
      if (container._vnode) {
        unmount(container._vnode, null, null, true)
      }
    } else {
      patch(container._vnode || null, vnode, container)
    }
    // 调用flushPostFlushCbs执行一些在patch中创建的钩子
    flushPostFlushCbs()
    // 保存根组件vnode
    container._vnode = vnode
  }
  // 返回渲染器
  return {
    render,
    hydrate,
    createApp: createAppAPI(render, hydrate)
  }
}

// createAppAPI创建并返回了createApp,这里也就是我们
// 调用createApp时真正执行的地方
// 传入的rootComponent为我们定义的根组件App,rootProps为根组件的props
function createAppAPI(render, hydrate) {
  return function createApp(rootComponent, rootProps = null) {
    // 通过createAppContext创建app上下文,保存全局的一些配置、组件指令等
    const context = createAppContext()
    // 存放插件
    const installedPlugins = new Set()

    let isMounted = false
    // 创建全局app对象,将传入的根组件的定义、参数等保存在app对象上
    // 并且定义了一些全局的方法,例如注册插件、注册全局组件、指令的方法等等
    // 这里我们主要分析mount方法
    const app: App = (context.app = {
      _component: rootComponent as ConcreteComponent,
      _props: rootProps,
      _container: null,
      _context: context,

      version,

      get config() {
        return context.config
      },

      set config(v) {
        if (__DEV__) {
          warn(
            `app.config cannot be replaced. Modify individual options instead.`
          )
        }
      },

      use(plugin: Plugin, ...options: any[]) {
        ...
      },

      mixin(mixin: ComponentOptions) {
        ...
      },

      component(name: string, component?: Component): any {
        ...
      },

      directive(name: string, directive?: Directive) {
        ...
      },

      mount(rootContainer: HostElement, isHydrate?: boolean): any {
        if (!isMounted) {
          //根据传入的根组件和参数生成根组件vnode
          const vnode = createVNode(
            rootComponent as ConcreteComponent,
            rootProps
          )
          // 将全局上下文保存在根组件vnode的appContext上
          vnode.appContext = context

          // HMR root reload
          if (__DEV__) {
            context.reload = () => {
              render(cloneVNode(vnode), rootContainer)
            }
          }

          if (isHydrate && hydrate) {
            hydrate(vnode as VNode<Node, Element>, rootContainer as any)
          } else {
            // 调用在baseCreateRenderer中定义的render方法渲染根vnode
            render(vnode, rootContainer)
          }
          isMounted = true
          app._container = rootContainer
          // for devtools and telemetry
          ;(rootContainer as any).__vue_app__ = app

          if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
            devtoolsInitApp(app, version)
          }

          return 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)\``
          )
        }
      },
      unmount() {
        ..
      },
      provide(key, value) {
        ...
      }
    })

    return app
  }
}

在mount方法中,调用了在baseCreateRenderer中定义的render方法渲染根组件vnode,可以看到在render方法 中执行了patch方法来处理根组件vnode,在分析patch过程之前,我们先来了解vnode的创建过程,以及Vue3.x中 对vnode做了哪些优化。

# 根组件的patch过程


// patch方法传入旧vnode n1、新vnode n2和父节点container
// 如果传入了anchor,则插入到这个节点之前,否则插入到末尾
const patch: PatchFn = (
  n1,
  n2,
  container,
  anchor = null,
  parentComponent = null,
  parentSuspense = null,
  isSVG = false,
  optimized = false
) => {
  // 如果旧vnode存在,并且新旧vnode的type、key不同
  // 则先unmount旧节点
  if (n1 && !isSameVNodeType(n1, n2)) {
    anchor = getNextHostNode(n1)
    unmount(n1, parentComponent, parentSuspense, true)
    n1 = null
  }

  if (n2.patchFlag === PatchFlags.BAIL) {
    optimized = false
    n2.dynamicChildren = null
  }
  // 拿到vnode的类型
  const { type, ref, shapeFlag } = n2
  // 根据vnode的类型处理不同的vnode
  // 而当前处理的vnode是根组件vnode,所以调用的是processComponent方法
  switch (type) {
    case Text:
      ...
    case Comment:
      ...
    case Static:
      ...
    case Fragment:
      ...
    default:
      if (shapeFlag & ShapeFlags.ELEMENT) {
       ...
      } else if (shapeFlag & ShapeFlags.COMPONENT) {
        processComponent(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          optimized
        )
      } else if (shapeFlag & ShapeFlags.TELEPORT) {
        ...
      } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
        ...
      } else if (__DEV__) {
        warn('Invalid VNode type:', type, `(${typeof type})`)
      }
  }

}

// 处理组件vnode
const processComponent = (
  n1: VNode | null,
  n2: VNode,
  container: RendererElement,
  anchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  isSVG: boolean,
  optimized: boolean
) => {
  // 如果旧节点不存在则调用mountComponent,执行创建组件的逻辑
  // 否则调用updateComponent更新组件
  if (n1 == null) {
    mountComponent(
      n2,
      container,
      anchor,
      parentComponent,
      parentSuspense,
      isSVG,
      optimized
    )
  } else {
    updateComponent(n1, n2, optimized)
  }
}

// 创建组件的逻辑
const mountComponent: MountComponentFn = (
  initialVNode,
  container,
  anchor,
  parentComponent,
  parentSuspense,
  isSVG,
  optimized
) => {
  // 创建组件实例
  const instance: ComponentInternalInstance = (initialVNode.component = createComponentInstance(
    initialVNode,
    parentComponent,
    parentSuspense
  ))
  ...
  // 处理组件vnode上的props、执行组件的setup方法
  setupComponent(instance)
  ...
  // 创建组件Effect
  setupRenderEffect(
    instance,
    initialVNode,
    container,
    anchor,
    parentSuspense,
    isSVG,
    optimized
  )
  ...
}

# 创建组件实例

export function createComponentInstance(
  vnode: VNode,
  parent: ComponentInternalInstance | null,
  suspense: SuspenseBoundary | null
) {
  // 拿到根组件的定义
  const type = vnode.type as ConcreteComponent
  // 拿到全局上下文
  const appContext =
    (parent ? parent.appContext : vnode.appContext) || emptyAppContext
  // 创建组件实例
  const instance: ComponentInternalInstance = {
    uid: uid++,
    vnode,
    type,
    parent,
    appContext,
    root: null!, // to be immediately set
    next: null,
    subTree: null!, // will be set synchronously right after creation
    update: null!, // will be set synchronously right after creation
    render: null,
    proxy: null,
    withProxy: null,
    effects: null,
    provides: parent ? parent.provides : Object.create(appContext.provides),
    accessCache: null!,
    renderCache: [],

    // state
    ctx: EMPTY_OBJ,
    data: EMPTY_OBJ,
    props: EMPTY_OBJ,
    attrs: EMPTY_OBJ,
    slots: EMPTY_OBJ,
    refs: EMPTY_OBJ,
    setupState: EMPTY_OBJ,
    setupContext: null,

    // suspense related
    suspense,
    asyncDep: null,
    asyncResolved: false,

    // lifecycle hooks
    // not using enums here because it results in computed properties
    isMounted: false,
    isUnmounted: false,
    isDeactivated: false,
    bc: null,
    c: null,
    bm: null,
    m: null,
    bu: null,
    u: null,
    um: null,
    bum: null,
    da: null,
    a: null,
    rtg: null,
    rtc: null,
    ec: null,
    emit: null as any, // to be set immediately
    emitted: null
  }
  if (__DEV__) {
    instance.ctx = createRenderContext(instance)
  } else {
    instance.ctx = { _: instance }
  }
  // 将根组件的实例保存在实例的root属性上
  instance.root = parent ? parent.root : instance
  // 将emit方法的this指向实例,并保存到emit属性上
  instance.emit = emit.bind(null, instance)

  return instance
}

# 处理组件vnode上的props、执行组件的setup方法

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

  const { props, children, shapeFlag } = instance.vnode
  //
  const isStateful = shapeFlag & ShapeFlags.STATEFUL_COMPONENT
  // 处理props
  initProps(instance, props, isStateful, isSSR)
  // 处理slot
  initSlots(instance, children)
  // 如果是非函数组件,调用setupStatefulComponent执行setup
  const setupResult = isStateful
    ? setupStatefulComponent(instance, isSSR)
    : undefined
  isInSSRComponentSetup = false
  return setupResult
}

function setupStatefulComponent(
  instance: ComponentInternalInstance,
  isSSR: boolean
) {
  // 拿到实例上的组件定义
  const Component = instance.type as ComponentOptions
  ...
  instance.accessCache = {}
  // 代理组件上下文,将生成的代理对象保存在proxy上
  instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
  const { setup } = Component
  if (setup) {
    // 如果定义了setup方法,先拿到保存在实例上的attrs、slots、emit
    const setupContext = (instance.setupContext =
      setup.length > 1 ? createSetupContext(instance) : null)
    // 设置currentInstance
    currentInstance = instance
    // 调用setup方法时暂定依赖收集
    pauseTracking()
    // 调用setup方法
    const setupResult = callWithErrorHandling(
      setup,
      instance,
      ErrorCodes.SETUP_FUNCTION,
      [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
    )
    resetTracking()
    currentInstance = null
    // 处理setup方法返回的结果
    handleSetupResult(instance, setupResult, isSSR)
  } else {
    // 如果没有定义setup,直接执行finishComponentSetup方法
    finishComponentSetup(instance, isSSR)
  }
}

// 处理setup方法返回的结果
export function handleSetupResult(
  instance: ComponentInternalInstance,
  setupResult: unknown,
  isSSR: boolean
) {
  // 如果setup返回的是一个render函数,那么将它保存在实例的render属性上
  if (isFunction(setupResult)) {
    // setup returned an inline render function
    instance.render = setupResult as InternalRenderFunction
  } else if (isObject(setupResult)) {
    // 如果返回的是一个对象,则代理这个对象,默认展开对象中的ref对象的值
    instance.setupState = proxyRefs(setupResult)
  } else if (__DEV__ && setupResult !== undefined) {
    warn(
      `setup() should return an object. Received: ${
        setupResult === null ? 'null' : typeof setupResult
      }`
    )
  }
  // 调用finishComponentSetup方法
  finishComponentSetup(instance, isSSR)
}


function finishComponentSetup(
  instance: ComponentInternalInstance,
  isSSR: boolean
) {
  const Component = instance.type as ComponentOptions

  // template / render function normalization
  if (__NODE_JS__ && isSSR) {
    ...
  } else if (!instance.render) {
    // 如果不存在render函数,根据定义的template调用compile方法生成render函数
    if (compile && Component.template && !Component.render) {
      Component.render = compile(Component.template, {
        isCustomElement: instance.appContext.config.isCustomElement,
        delimiters: Component.delimiters
      })
    }
    // 将生产的render函数保存到实例render属性上
    instance.render = (Component.render || NOOP) as InternalRenderFunction
  }
}

# 创建组件Effect、patch组件子树

const setupRenderEffect: SetupRenderEffectFn = (
  instance,
  initialVNode,
  container,
  anchor,
  parentSuspense,
  isSVG,
  optimized
) => {
  // 创建Effect,并保存在实例的updata方法上,
  // Effect在创建的时候会先执行本身
  instance.update = effect(function componentEffect() {
    if (!instance.isMounted) {
      // 创建组件逻辑
      let vnodeHook: VNodeHook | null | undefined
      const { el, props } = initialVNode
      const { bm, m, parent } = instance

      // 调用beforeMount钩子
      if (bm) {
        invokeArrayFns(bm)
      }
      // 调用vnode上的beforeMount钩子
      if ((vnodeHook = props && props.onVnodeBeforeMount)) {
        invokeVNodeHook(vnodeHook, parent, initialVNode)
      }
      // 创建子树vnode
      const subTree = (instance.subTree = renderComponentRoot(instance))
      ...
      // 调用patch执行子树vnode的patch过程
      patch(
        null,
        subTree,
        container,
        anchor,
        instance,
        parentSuspense,
        isSVG
      )
      // 将生成的真实DOM节点保存在组件vnode的el属性上
      initialVNode.el = subTree.el
      // 调用mountd钩子
      if (m) {
        queuePostRenderEffect(m, parentSuspense)
      }
      // 调用vnode的mountd钩子
      if ((vnodeHook = props && props.onVnodeMounted)) {
        queuePostRenderEffect(() => {
          invokeVNodeHook(vnodeHook!, parent, initialVNode)
        }, parentSuspense)
      }
      instance.isMounted = true
    } else {
      // 更新组件逻辑
    }
  }, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
  // #1801 mark it to allow recursive updates
  ;(instance.update as SchedulerJob).allowRecurse = true
}