Vue3源码之createApp
前言Vue3版本源码架构与Vue2版本存在非常大的不同,从使用方式的角度来讲,对它们可以简单概括为:Vue2版本是命令方式使用,通过new Vue去创建Vue实例,组件中使用选项组合代码逻辑Vue3版本将命令和功能API函数化,通过createApp去创建应用实例,组件中通过组合式API和选项组合代码这里需要区分与Vue2中的一些概念,Vue2中Vue实例实际上就是组件实例,而Vue3中creat
前言
Vue3版本源码架构与Vue2版本存在非常大的不同,从使用方式的角度来讲,对它们可以简单概括为:
- Vue2版本是命令方式使用,通过new Vue去创建Vue实例,组件中使用选项组合代码逻辑
- Vue3版本将命令和功能API函数化,通过createApp去创建应用实例,组件中通过组合式API和选项组合代码
这里需要区分与Vue2中的一些概念,Vue2中Vue实例实际上就是组件实例,而Vue3中createApp则是创建一个上下文,称之为应用实例。组件实例是通过其他方式来获得的,例如Vue3中mount挂载后就会返回组件实例。(Vue 3.1.1版本)
createApp执行逻辑
在具体梳理createApp之前,聊聊Vue3构建输出的文件结构,不同于Vue2构建输出的UMD规范的文件,Vue3输出的文件主体就是一个IIFE函数:
var Vue = (function(exports) {
...
exports.createApp = createApp;
...
})({});
实际上就是输出一个名为Vue的对象,该对象提供createApp函数等。
Vue.createApp的执行实际上就是调用一个createApp函数,该函数的逻辑比较清晰,这里直接贴出代码:
const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args);
{
injectNativeTagCheck(app);
injectCompilerOptionsCheck(app);
}
const { mount } = app;
app.mount = (containerOrSelector) => {
// 相关处理
}
return app;
});
从上面逻辑可知,createApp函数的功能是返回一个app对象,并且会重写mount函数。而创建app对象又会调用其他函数来完成,相关逻辑在后面会详细梳理。
创建Renderer对象
在Vue3中存在Renderer对象的概念,实际上就是渲染器,顾名思义渲染器就是负责节点渲染。
渲染器是延迟创建的,只有在调用ensureRenderer或ensureHydrationRenderer函数才会创建,而且是单例模式。当调用Vue.createApp就会调用ensureRenderer函数,其逻辑具体如下:
function ensureRenderer() {
return renderer ||
(renderer = createRenderer(rendererOptions));
}
function createRenderer(options) {
return baseCreateRenderer(options);
}
其中rendererOptions是渲染器相关的参数,具体内容是分为两类:
- patchProp相关:实际上是组件相关属性、事件等更新相关操作,即patch阶段的处理class、style、on事件绑定等
- node操作相关的:节点对象的操作,例如插入、移除等
实际上Vue3允许自定义渲染器,对外暴露了createRenderer,在官网文档中有对其的定义描述:
createRenderer 函数接受两个泛型参数: HostNode 和 HostElement,对应于宿主环境中的 Node 和 Element 类型。例如,对于 runtime-dom,HostNode 将是 DOM Node 接口,HostElement 将是 DOM Element 接口。
createRenderer函数背后是调用baseCreateRenderer。
baseCreateRenderer函数
该函数的逻辑简单来说就是返回一个对象,该对象被称为渲染器对象,该对象就3个方法:
function baseCreateRenderer(options, createHydrationFns) {
// 相关逻辑,其中包含相关patch阶段相关处理的函数定义
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
};
}
结合Vue2版本的逻辑可以知道,渲染器一部分承担了patch阶段的diff算法的功能和VNode到真实DOM的处理操作。这里先暂时不管这部分的功能,主要看createAppAPI的具体处理逻辑。createAppAPI就是返回一个createApp函数而已:
function createAppAPI(render, hydrate) {
return function createApp(rootComponent, rootProps = null) {
// 相关逻辑
}
}
渲染器的createApp处理
Vue.createApp最后就是调用渲染器对象Renderer的createApp函数,其逻辑代码具体如下:
function createApp(rootComponent, rootProps = null) {
// rootProps必须是对象或null
if (rootProps != null && !isObject(rootProps)) {
warn(`root props passed to app.mount() must be an object.`);
rootProps = null;
}
// 创建应用上下文
const context = createAppContext();
const installedPlugins = new Set();
let isMounted = false;
// 生成具体对象,该对象提供具体的应用API和相关属性
const app = (context.app = {})
return app;
}
实际上主要逻辑关注点就两处:
- createAppContext函数调用创建应用上下文对象
- 应用对象app的具体内容
调用createAppContext函数创建应用上下文对象
createAppContext函数的具体逻辑如下:
function createAppContext() {
return {
// 应用实例
app: null,
// 应用配置项
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()
};
}
应用实例对象app属性
Vue.createApp函数功能就是返回应用实例对象的,该对象处理提供应用API外,还存在一些内部属性:
const app = (context.app = {
_uid: uid$1++,
_component: rootComponent,
_props: rootProps,
_container: null,
_context: context,
_instance: null,
version,
get config() { return context.config; },
set config(v) {
{
warn(`app.config cannot be replaced. Modify individual options instead.`);
}
},
// 应用API
use(plugin, ...options) {},
mixin(mixin) {},
component(name, component) {},
directive(name, directive) {},
mount(rootContainer, isHydrate, isSVG) {},
unmount() {},
provide(key, value) {}
});
相关应用API的说明可以具体看Vue3官方文档,而相关内部属性实际上也比较清晰,例如:
- _context:表示应用上下文实例
- _component:根组件
- _container:挂载点容器Node
- _instance:不出意外应该就是根组件实例对象了
config的额外处理
当Vue.createApp生成应用实例对象后,会针对上下文中config配置对象做额外的处理,具体代码如下:
injectNativeTagCheck(app);
injectCompilerOptionsCheck(app);
函数的定义名称是非常清晰的,上面2个函数的具体逻辑实际上就是向config对象中增加:
- isNativeTag的检查函数:判断是否是HTML标签或SVG标签
- isCustomElement 和 compilerOptions属性,具体是用来做什么这里暂不关心
总结
Vue.createApp是用来创建应用实例的,该应用实例提供相关的应用API和维护相关的状态。在应用实例创建过程中,会创建一个渲染器对象Renderer,针对渲染器对象有如下几点说明:
- 渲染器对象的创建是惰性的,并且是单例模式
- Vue3对外暴露了createRenderer API,允许开发者自定义渲染器
- 渲染器承担Vue3视图渲染相关的功能,其中包含patch阶段的相关处理(diff算法的实现 和 DOM替换等)、应用实例的创建
对比Vue2根组件的处理逻辑(new Vue实际上就是组件实例生成的过程)中data、props、computed等相关处理逻辑,Vue3中相关处理延后到mount挂载阶段。
更多推荐
所有评论(0)