Skip to content

1.4 启动全景图

本节将深入分析Vue.js 3从应用创建到首次渲染的完整启动流程,通过源码解析和架构图展示Vue 3的启动机制。

1.4.1 启动流程概览

Vue 3的启动过程可以分为四个核心阶段:

  1. 应用实例创建 - createApp()
  2. 应用挂载 - app.mount()
  3. 渲染器初始化 - 创建渲染器和patch函数
  4. 组件系统启动 - 根组件的创建和渲染

1.4.2 核心源码文件分析

入口文件架构

是Vue的主入口文件,负责:
typescript
// 全量构建入口
import { initDev } from './dev'
import { compile } from '@vue/compiler-dom'
import { registerRuntimeCompiler, RenderFunction, warn } from '@vue/runtime-dom'
import * as runtimeDom from '@vue/runtime-dom'
import { isString, NOOP, generateCodeFrame, extend } from '@vue/shared'

// 编译缓存
const compileCache: Record<string, RenderFunction> = Object.create(null)

// 模板编译函数
function compileToFunction(
  template: string | HTMLElement,
  options?: CompilerOptions,
): RenderFunction {
  // 模板处理和缓存逻辑
}

// 注册运行时编译器
registerRuntimeCompiler(compileToFunction)

// 导出所有运行时API
export * from '@vue/runtime-dom'

DOM运行时层

提供DOM特定的渲染能力:
typescript
// 渲染器选项配置
const rendererOptions = extend({ patchProp }, nodeOps)

// 确保渲染器实例
let renderer: Renderer<Element | ShadowRoot> | HydrationRenderer

function ensureRenderer() {
  return (
    renderer ||
    (renderer = createRenderer<Node, Element>(rendererOptions))
  )
}

// 创建应用实例
export const createApp = ((...args) => {
  const app = ensureRenderer().createApp(...args)
  
  const { mount } = app
  app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
    const container = normalizeContainer(containerOrSelector)
    if (!container) return
    
    const component = app._component
    if (!isFunction(component) && !component.render && !component.template) {
      component.template = container.innerHTML
    }
    
    // 清空容器内容
    container.innerHTML = ''
    const proxy = mount(container, false, resolveRootNamespace(container))
    if (container instanceof Element) {
      container.removeAttribute('v-cloak')
      container.setAttribute('data-v-app', '')
    }
    return proxy
  }
  
  return app
}) as CreateAppFunction<Element>

1.4.3 应用创建流程详解

createApp() 实现分析

中的 函数:
typescript
export function createAppAPI<HostElement>(
  render: RootRenderFunction<HostElement>,
  hydrate?: RootHydrateFunction,
): CreateAppFunction<HostElement> {
  return function createApp(rootComponent, rootProps = null) {
    // 根组件处理
    if (!isFunction(rootComponent)) {
      rootComponent = extend({}, rootComponent)
    }
    
    // 根属性校验
    if (rootProps != null && !isObject(rootProps)) {
      __DEV__ && warn(`root props passed to app.mount() must be an object.`)
      rootProps = null
    }
    
    // 创建应用上下文
    const context = createAppContext()
    const installedPlugins = new WeakSet()
    const pluginCleanupFns: Array<() => any> = []
    
    let isMounted = false
    
    const app: App = (context.app = {
      _uid: uid++,
      _component: rootComponent as ConcreteComponent,
      _props: rootProps,
      _container: null,
      _context: context,
      _instance: null,
      
      version,
      
      get config() {
        return context.config
      },
      
      // 插件系统
      use(plugin: Plugin, ...options: any[]) {
        // 插件安装逻辑
      },
      
      // 全局组件注册
      component(name: string, component?: Component): any {
        // 组件注册逻辑
      },
      
      // 全局指令注册
      directive(name: string, directive?: Directive) {
        // 指令注册逻辑
      },
      
      // 应用挂载
      mount(rootContainer: HostElement, isHydrate?: boolean, namespace?: boolean | ElementNamespace): any {
        // 挂载逻辑详见下节
      },
      
      // 应用卸载
      unmount() {
        // 卸载逻辑
      },
      
      // 依赖注入
      provide(key, value) {
        // 提供依赖
      }
    })
    
    return app
  }
}

应用上下文创建

函数创建应用级别的上下文:
typescript
export function createAppContext(): AppContext {
  return {
    app: null as any,
    config: {
      isNativeTag: NO,
      performance: false,
      globalProperties: {},
      optionMergeStrategies: {},
      errorHandler: undefined,
      warnHandler: undefined,
      compilerOptions: {}
    },
    mixins: [],
    components: {},
    directives: {},
    provides: Object.create(null),
    optionsCache: new WeakMap(),
    propsCache: new WeakMap(),
    emitsCache: new WeakMap()
  }
}

1.4.4 应用挂载流程详解

mount() 方法实现

typescript
mount(
  rootContainer: HostElement,
  isHydrate?: boolean,
  namespace?: boolean | ElementNamespace,
): 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.`,
      )
    }
    
    // 创建根VNode
    const vnode = app._ceVNode || createVNode(rootComponent, rootProps)
    
    // 设置应用上下文
    vnode.appContext = context
    
    // 命名空间处理
    if (namespace === true) {
      namespace = 'svg'
    } else if (namespace === false) {
      namespace = undefined
    }
    
    // HMR支持
    if (__DEV__) {
      context.reload = () => {
        const cloned = cloneVNode(vnode)
        cloned.el = null
        render(cloned, rootContainer, namespace as ElementNamespace)
      }
    }
    
    // 渲染或水合
    if (isHydrate && hydrate) {
      hydrate(vnode as VNode<Node, Element>, rootContainer as any)
    } else {
      render(vnode, rootContainer, namespace)
    }
    
    // 标记已挂载
    isMounted = true
    app._container = rootContainer
    ;(rootContainer as any).__vue_app__ = app
    
    // 开发工具支持
    if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
      app._instance = vnode.component
      devtoolsInitApp(app, version)
    }
    
    return getComponentPublicInstance(vnode.component!)
  }
}

1.4.5 渲染器初始化

渲染器创建

中的 函数:
typescript
function baseCreateRenderer(
  options: RendererOptions,
  createHydrationFns?: typeof createHydrationFunctions,
): any {
  // 特性标志初始化
  if (__ESM_BUNDLER__ && !__TEST__) {
    initFeatureFlags()
  }
  
  // 设置全局Vue标识
  const target = getGlobalThis()
  target.__VUE__ = true
  
  // 开发工具支持
  if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
    setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__, target)
  }
  
  // 解构渲染器选项
  const {
    insert: hostInsert,
    remove: hostRemove,
    patchProp: hostPatchProp,
    createElement: hostCreateElement,
    createText: hostCreateText,
    createComment: hostCreateComment,
    setText: hostSetText,
    setElementText: hostSetElementText,
    parentNode: hostParentNode,
    nextSibling: hostNextSibling,
    setScopeId: hostSetScopeId = NOOP,
    insertStaticContent: hostInsertStaticContent,
  } = options
  
  // 核心patch函数
  const patch: PatchFn = (
    n1,
    n2,
    container,
    anchor = null,
    parentComponent = null,
    parentSuspense = null,
    namespace = undefined,
    slotScopeIds = null,
    optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren,
  ) => {
    // patch逻辑详见下节
  }
  
  // 其他核心函数定义...
  
  // 根渲染函数
  const render: RootRenderFunction = (vnode, container, namespace) => {
    if (vnode == null) {
      if (container._vnode) {
        unmount(container._vnode, null, null, true)
      }
    } else {
      patch(
        container._vnode || null,
        vnode,
        container,
        null,
        null,
        null,
        namespace,
      )
    }
    container._vnode = vnode
    
    // 刷新调度队列
    if (!isFlushing) {
      isFlushing = true
      flushPreFlushCbs()
      flushPostFlushCbs()
      isFlushing = false
    }
  }
  
  // 返回渲染器实例
  return {
    render,
    hydrate,
    createApp: createAppAPI(render, hydrate),
  }
}

patch函数核心逻辑

typescript
const patch: PatchFn = (
  n1,
  n2,
  container,
  anchor = null,
  parentComponent = null,
  parentSuspense = null,
  namespace = undefined,
  slotScopeIds = null,
  optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren,
) => {
  // 相同VNode直接返回
  if (n1 === n2) {
    return
  }
  
  // 类型不同,卸载旧节点
  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
  }
  
  const { type, ref, shapeFlag } = n2
  
  // 根据节点类型分发处理
  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, namespace)
      } else if (__DEV__) {
        patchStaticNode(n1, n2, container, namespace)
      }
      break
    case Fragment:
      processFragment(
        n1,
        n2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        namespace,
        slotScopeIds,
        optimized,
      )
      break
    default:
      if (shapeFlag & ShapeFlags.ELEMENT) {
        processElement(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          namespace,
          slotScopeIds,
          optimized,
        )
      } else if (shapeFlag & ShapeFlags.COMPONENT) {
        processComponent(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          namespace,
          slotScopeIds,
          optimized,
        )
      }
  }
  
  // 处理ref
  if (ref != null && parentComponent) {
    setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
  }
}

1.4.6 组件系统启动

组件处理流程

函数负责组件的挂载和更新:
typescript
const processComponent = (
  n1: VNode | null,
  n2: VNode,
  container: RendererElement,
  anchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  namespace: ElementNamespace,
  slotScopeIds: string[] | null,
  optimized: boolean,
) => {
  n2.slotScopeIds = slotScopeIds
  
  if (n1 == null) {
    // 新组件挂载
    if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
      // KeepAlive组件激活
      ;(parentComponent!.ctx as KeepAliveContext).activate(
        n2,
        container,
        anchor,
        namespace,
        optimized,
      )
    } else {
      // 普通组件挂载
      mountComponent(
        n2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        namespace,
        optimized,
      )
    }
  } else {
    // 组件更新
    updateComponent(n1, n2, optimized)
  }
}

组件挂载流程

函数实现组件的完整挂载:
typescript
const mountComponent: MountComponentFn = (
  initialVNode,
  container,
  anchor,
  parentComponent,
  parentSuspense,
  namespace: ElementNamespace,
  optimized,
) => {
  // 创建组件实例
  const instance: ComponentInternalInstance =
    initialVNode.component = createComponentInstance(
      initialVNode,
      parentComponent,
      parentSuspense,
    )
  
  // HMR支持
  if (__DEV__ && instance.type.__hmrId) {
    registerHMR(instance)
  }
  
  // 性能测量
  if (__DEV__) {
    pushWarningContext(initialVNode)
    startMeasure(instance, `mount`)
  }
  
  // KeepAlive支持
  if (isKeepAlive(initialVNode)) {
    ;(instance.ctx as KeepAliveContext).renderer = internals
  }
  
  // 组件设置
  if (__DEV__) {
    startMeasure(instance, `init`)
  }
  setupComponent(instance, false, optimized)
  if (__DEV__) {
    endMeasure(instance, `init`)
  }
  
  // 异步组件处理
  if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
    parentSuspense &&
      parentSuspense.registerDep(instance, setupRenderEffect, optimized)
    
    if (!initialVNode.el) {
      const placeholder = (instance.subTree = createVNode(Comment))
      processCommentNode(null, placeholder, container!, anchor)
      initialVNode.placeholder = placeholder.el
    }
  } else {
    // 设置渲染副作用
    setupRenderEffect(
      instance,
      initialVNode,
      container,
      anchor,
      parentSuspense,
      namespace,
      optimized,
    )
  }
  
  if (__DEV__) {
    popWarningContext()
    endMeasure(instance, `mount`)
  }
}

渲染副作用设置

函数建立组件的响应式渲染:
typescript
const setupRenderEffect: SetupRenderEffectFn = (
  instance,
  initialVNode,
  container,
  anchor,
  parentSuspense,
  namespace: ElementNamespace,
  optimized,
) => {
  const componentUpdateFn = () => {
    if (!instance.isMounted) {
      // 组件挂载阶段
      let vnodeHook: VNodeHook | null | undefined
      const { el, props } = initialVNode
      const { bm, m, parent, root, type } = instance
      
      // 禁用递归更新
      toggleRecurse(instance, false)
      
      // beforeMount钩子
      if (bm) {
        invokeArrayFns(bm)
      }
      
      // 父组件beforeMount钩子
      if (
        !isAsyncWrapperVNode &&
        (vnodeHook = props && props.onVnodeBeforeMount)
      ) {
        invokeVNodeHook(vnodeHook, parent, initialVNode)
      }
      
      // 启用递归更新
      toggleRecurse(instance, true)
      
      // 渲染组件子树
      if (__DEV__) {
        startMeasure(instance, `render`)
      }
      const subTree = (instance.subTree = renderComponentRoot(instance))
      if (__DEV__) {
        endMeasure(instance, `render`)
      }
      
      // patch子树
      if (__DEV__) {
        startMeasure(instance, `patch`)
      }
      patch(
        null,
        subTree,
        container,
        anchor,
        instance,
        parentSuspense,
        namespace,
      )
      if (__DEV__) {
        endMeasure(instance, `patch`)
      }
      
      // 缓存根元素
      initialVNode.el = subTree.el
      
      // mounted钩子
      if (m) {
        queuePostRenderEffect(m, parentSuspense)
      }
      
      // 父组件mounted钩子
      if (
        !isAsyncWrapperVNode &&
        (vnodeHook = props && props.onVnodeMounted)
      ) {
        const scopedNext = initialVNode
        queuePostRenderEffect(
          () => invokeVNodeHook(vnodeHook!, parent, scopedNext),
          parentSuspense,
        )
      }
      
      // 激活KeepAlive组件
      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)
      }
      
      instance.isMounted = true
      
      // 开发工具支持
      if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
        devtoolsComponentAdded(instance)
      }
      
      // 清理引用
      initialVNode = container = anchor = null as any
    } else {
      // 组件更新阶段
      // 更新逻辑...
    }
  }
  
  // 创建响应式副作用
  const effect = (instance.effect = new ReactiveEffect(componentUpdateFn))
  const update: SchedulerJob = (instance.update = () => {
    if (effect.dirty) {
      effect.run()
    }
  })
  update.i = instance
  update.id = instance.uid
  
  // 允许递归更新
  toggleRecurse(instance, true)
  
  // 执行初始更新
  update()
}

1.4.7 启动流程架构图

1.4.8 关键时序图

1.4.9 性能优化要点

编译时优化

  1. 静态提升 - 静态节点在编译时被提升到渲染函数外部
  2. 补丁标记 - 动态内容标记,减少运行时比较
  3. 块级优化 - 将动态节点收集到块中,跳过静态部分

运行时优化

  1. 组件实例复用 - KeepAlive组件缓存机制
  2. 异步组件 - 代码分割和懒加载
  3. 调度优化 - 批量更新和优先级调度
  4. 内存管理 - 及时清理引用,避免内存泄漏

1.4.10 启动流程总结

Vue 3的启动流程体现了现代前端框架的设计理念:

  1. 分层架构 - 清晰的运行时层次划分
  2. 响应式驱动 - 基于Proxy的响应式系统
  3. 组合式设计 - 渲染器、调度器、响应式系统的有机结合
  4. 性能优先 - 编译时和运行时的双重优化
  5. 开发体验 - 完善的开发工具和错误处理

通过深入理解这个启动流程,开发者可以更好地:

  • 优化应用性能
  • 调试复杂问题
  • 扩展Vue功能
  • 理解框架设计思想

这个全景图为后续章节的深入学习奠定了坚实的基础。


微信公众号二维码