vue3源码解析:创建vue实例过程

152 阅读3分钟

上文,我们分析了 vue3 源码的项目结构,以及 packages 目录下的各个模块的作用和联系,本节从 vue3 的初始化流程开始分析runtime-core模块,当我们执行const app = createApp(App)发生了什么?

项目结构

{FAABDB9C-48C2-409A-9601-760F82034F51}.png

查看 README.md 文件

{8BCD172C-5978-4461-846B-EB98C4329AA3}.png

显然 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) 时:

  1. 首先从 vue 包导入 createApp
  2. 这个 createApp 实际上来自 @vue/runtime-dom
  3. 而 runtime-dom 中的 createApp 又会调用 runtime-core 中的实现
  4. 最终在 apiCreateApp.ts 中完成实际的应用创建工作

这种设计的好处是:

  1. 实现了关注点分离
  2. 使得核心逻辑可以被不同平台复用
  3. 保持了良好的可扩展性

源码细节

让我们深入查看 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: 依赖注入的数据
  • 各种缓存(optionsCachepropsCacheemitsCache

创建应用实例

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.`)
  }
}
  • 提供对应用配置的访问
  • 防止整个配置对象被替换

总结

实现特点

  1. 全局状态隔离:每个应用实例都有自己的上下文,避免了全局状态污染
  2. 插件系统:提供了强大的插件机制,支持扩展应用功能
  3. 开发体验:在开发环境提供了丰富的警告信息
  4. 类型安全:通过 TypeScript 提供了完整的类型支持
  5. 可扩展性:通过提供各种 API,使应用可以被轻松扩展

这就是 Vue 应用创建的核心实现,它为整个应用提供了基础架构和运行环境。