上文,我们分析了 vue3 源码的项目结构,以及 packages 目录下的各个模块的作用和联系,本节从 vue3 的初始化流程开始分析runtime-core模块,当我们执行const app = createApp(App)发生了什么?
项目结构
查看 README.md 文件
显然 createApp 是在这个模块中,咱们从 createApp 的执行开始分析。
核心流程
总体流程
我们可以看到 createApp 是在 runtime-dom 中定义的,但它依赖于 runtime-core 中的一些功能。让我们继续查看 runtime-core 中的相关代码:
1. 最终导出位置:
createApp 最终是从 @vue/runtime-dom 包导出的。具体在 packages/runtime-dom/src/index.ts 中:
export const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args)
// ...
})
2. 实际实现位置:
实际的 createApp 实现在 packages/runtime-core/src/apiCreateApp.ts 中,通过 createAppAPI 函数创建。
3. 调用链路:
用户代码 createApp(App)
↓
runtime-dom/index.ts 的 createApp
↓
ensureRenderer() 确保渲染器存在
↓
renderer.createApp(...args)
↓
runtime-core/apiCreateApp.ts 中的具体实现
4. 核心实现:
在 apiCreateApp.ts 中,createApp 函数会:
- 创建应用实例
- 设置应用配置
- 提供各种应用级 API(如 use、component、directive 等)
- 处理根组件的挂载
所以,当你在项目中使用 createApp(App) 时:
- 首先从 vue 包导入 createApp
- 这个 createApp 实际上来自 @vue/runtime-dom
- 而 runtime-dom 中的 createApp 又会调用 runtime-core 中的实现
- 最终在 apiCreateApp.ts 中完成实际的应用创建工作
这种设计的好处是:
- 实现了关注点分离
- 使得核心逻辑可以被不同平台复用
- 保持了良好的可扩展性
源码细节
让我们深入查看 apiCreateApp.ts 中的具体实现:
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,
// ... 其他 API 方法实现
});
return app;
};
}
这是创建 Vue 应用实例的核心代码,让我们深入分析其中的具体实现:
创建应用上下文
const context = createAppContext();
这个上下文包含:
app: 应用实例config: 应用配置(如性能追踪、错误处理等)mixins: 全局混入components: 全局组件directives: 全局指令provides: 依赖注入的数据- 各种缓存(
optionsCache、propsCache、emitsCache)
创建应用实例
const app: App = (context.app = {
_uid: uid++, // 唯一标识
_component: rootComponent, // 根组件
_props: rootProps, // 根组件的 props
_container: null, // 挂载容器
_context: context, // 应用上下文
_instance: null, // 根组件实例
version, // Vue 版本
});
提供核心 API
应用实例提供了一系列方法:
use 方法 - 用于安装插件:
use(plugin: Plugin, ...options: any[]) {
if (installedPlugins.has(plugin)) {
__DEV__ && warn(`Plugin has already been applied to target app.`)
} else if (plugin && isFunction(plugin.install)) {
installedPlugins.add(plugin)
plugin.install(app, ...options)
} else if (isFunction(plugin)) {
installedPlugins.add(plugin)
plugin(app, ...options)
}
return app
}
其他核心方法:
component方法 - 注册全局组件directive方法 - 注册全局指令mount方法 - 挂载应用unmount方法 - 卸载应用provide方法 - 提供依赖注入的值
安全检查和类型验证
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 installedPlugins = new WeakSet();
const pluginCleanupFns: Array<() => any> = [];
- 使用 WeakSet 记录已安装的插件
- 管理插件的清理函数
配置管理
get config() {
return context.config
},
set config(v) {
if (__DEV__) {
warn(`app.config cannot be replaced. Modify individual options instead.`)
}
}
- 提供对应用配置的访问
- 防止整个配置对象被替换
总结
实现特点
- 全局状态隔离:每个应用实例都有自己的上下文,避免了全局状态污染
- 插件系统:提供了强大的插件机制,支持扩展应用功能
- 开发体验:在开发环境提供了丰富的警告信息
- 类型安全:通过 TypeScript 提供了完整的类型支持
- 可扩展性:通过提供各种 API,使应用可以被轻松扩展
这就是 Vue 应用创建的核心实现,它为整个应用提供了基础架构和运行环境。