Appearance
1.4 启动全景图
本节将深入分析Vue.js 3从应用创建到首次渲染的完整启动流程,通过源码解析和架构图展示Vue 3的启动机制。
1.4.1 启动流程概览
Vue 3的启动过程可以分为四个核心阶段:
- 应用实例创建 -
createApp() - 应用挂载 -
app.mount() - 渲染器初始化 - 创建渲染器和patch函数
- 组件系统启动 - 根组件的创建和渲染
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 性能优化要点
编译时优化
- 静态提升 - 静态节点在编译时被提升到渲染函数外部
- 补丁标记 - 动态内容标记,减少运行时比较
- 块级优化 - 将动态节点收集到块中,跳过静态部分
运行时优化
- 组件实例复用 - KeepAlive组件缓存机制
- 异步组件 - 代码分割和懒加载
- 调度优化 - 批量更新和优先级调度
- 内存管理 - 及时清理引用,避免内存泄漏
1.4.10 启动流程总结
Vue 3的启动流程体现了现代前端框架的设计理念:
- 分层架构 - 清晰的运行时层次划分
- 响应式驱动 - 基于Proxy的响应式系统
- 组合式设计 - 渲染器、调度器、响应式系统的有机结合
- 性能优先 - 编译时和运行时的双重优化
- 开发体验 - 完善的开发工具和错误处理
通过深入理解这个启动流程,开发者可以更好地:
- 优化应用性能
- 调试复杂问题
- 扩展Vue功能
- 理解框架设计思想
这个全景图为后续章节的深入学习奠定了坚实的基础。
