源码篇 使用及分析 Vue 全局 API
电梯Vue 源码源码篇 剖析 Vue2 双向绑定原理源码篇 使用及分析 Vue 全局 API源码篇 虚拟DOM源码篇 模板编译源码篇 实例方法源码篇 生命周期持续更新中...Vue Router 4 源码源码篇 Vue Router 4 上篇源码篇 Vue Router 4 中篇源码篇 Vue Router 4 下篇源码拉取步骤可以看这篇文章有讲解下面直接进入正题源码篇 剖析 Vue 双向绑定原理-CSDN博客前言在《源码篇 剖析 Vue 双向绑定原理》这篇文章中提到了 Vue 中不管是可以处理 Object 数据的 Object.defineProperty还是 处理 Array 数据的 拦截器都无法监听到新增属性或删除属性那时我们提到了 Vue.set() 和 Vue.delete()以及使用它们两个如何解决这个问题。本文将扩展其他的全局 API针对使用方法及源码进行分析说明。正文1. Vue.complie用法用于将模板字符串编译成渲染函数。Vue 3 中 Vue.compile() 被移除直接通过组件定义或 createApp 进行渲染。Vue2 示例const renderFunction Vue.compile(div{{ message }}/div); new Vue({ data: { message: hello }, render: renderFunction.render, staticRenderFns: renderFunction.staticRenderFns })render 和 staticRenderFns 在源码中有定义源码分析在源码文件 src/platforms/web/runtime-with-compiler.ts 中有一句源码Vue.compile compileToFunctions它内部调用了 compileToFunctions 方法该代码片段源码位置src/compiler/index.ts此处只展示相关的代码部分export const createCompiler createCompilerCreator(function baseCompile( template: string, options: CompilerOptions ): CompiledResult { const ast parse(template.trim(), options) if (options.optimize ! false) { optimize(ast, options) } const code generate(ast, options) return { ast, render: code.render, staticRenderFns: code.staticRenderFns } })模板解析阶段通过正则等方式解析 template 模板中的指令、class、style 等数据形成ASTconst ast parse(template.trim(), options)优化阶段遍历AST找出其中的静态节点并打上标记if (options.optimize ! false) { optimize(ast, options) }代码生成阶段将AST转换成渲染函数const code generate(ast, options)总结更详细的内容将在后续文章描述模板编译内容的时候说明。Vue.directive、Vue.filter 和 Vue.component 源码写在一起因此将在最后位置一起分析2. Vue.filter用法用于注册、获取全局过滤器接收两个参数过滤器 id 和过滤器的定义。注册将定义好的过滤器存放在某个位置获取根据过滤器id在存放过滤器的位置读取过滤器。Vue3 不再支持 Vue.filter()可以使用计算属性或方法替代。使用{{ message | capitalize }}Vue2 示例// 注册 Vue.filter(capitalize, function(value) { if (!value) return ; return value.charAt(0).toUpperCase() value.slice(1); }); // getter返回已注册的过滤器 const myFilter Vue.filter(my-filter);Vue.options[filters]用来存放全局过滤器。根据是否传入 definition 参数来判断注册过滤器和获取过滤器。如果没有传入 definition 参数则表示为获取过滤器根据过滤器 id 读取过滤器如果传入 definition 参数则表示为注册过滤器就将其保存在this.options[filters]中。Vue.options Object.create(null) Vue.options[filters] Object.create(null) Vue.filter function (id,definition) { if (!definition) { return this.options[filters][id] } else { this.options[filters][id] definition return definition } }3. Vue.component用法用于注册、获取全局组件。注Vue2、Vue3 使用方法相同Vue.component(my-component, { template: divHello from my component!/div }); const myComponent Vue.component(my-component);和 Vue.filter 同理根据是否传入 definition 参数来判断注册组件和获取组件。如果是注册组件在非生产环境下会校验组件的name值是否合法。Vue.options Object.create(null) Vue.options[components] Object.create(null) Vue.filter function (id,definition) { if (!definition) { return this.options[components][id] } else { if (process.env.NODE_ENV ! production type component) { validateComponentName(id) } if (type component isPlainObject(definition)) { definition.name definition.name || id definition this.options._base.extend(definition) } this.options[components][id] definition return definition } }4. Vue.directive用法用于注册、获取全局指令。Vue2 示例Vue.directive(focus, { bind: function () {}, inserted: function () {}, update: function () {}, componentUpdated: function () {}, unbind: function () {} }) // 注册 (指令函数) Vue.directive(focus, function () { // 这里会被 bind 和 update 调用 }) // getter const myDirective Vue.directive(focus)Vue3示例Vue.directive(focus, { beforeMount(el) { el.focus(); } });和 Vue.filter 同理根据是否传入 definition 参数来判断注册指令和获取指令。如果是注册指令继续判断 definition 参数是否是一个函数如果是则默认监听 bind 和 update。如果 definition 参数不是一个函数即认为它是用户自定义的指令对象直接将其保存在 this.options[directives] 中。Vue.options Object.create(null) Vue.options[directives] Object.create(null) Vue.directive function (id,definition) { if (!definition) { return this.options[directives][id] } else { if (type directive typeof definition function) { definition { bind: definition, update: definition } } this.options[directives][id] definition return definition } }上面我们已经分别分析了内部实现原理下面是完整实现代码源码位置1src/shared/constants.tsexport const ASSET_TYPES [component, directive, filter] as const源码位置2src/core/global-api/index.tsVue.options Object.create(null) ASSET_TYPES.forEach(type { Vue.options[type s] Object.create(null) })源码位置3src/core/global-api/assets.tsASSET_TYPES.forEach(type { Vue[type] function ( id: string, definition?: Function | Object ): Function | Object | void { if (!definition) { return this.options[type s][id] } else { if (__DEV__ type component) { validateComponentName(id) } if (type component isPlainObject(definition)) { definition.name definition.name || id definition this.options._base.extend(definition) } if (type directive isFunction(definition)) { definition { bind: definition, update: definition } } this.options[type s][id] definition return definition } } })具体的实现原理我们将在《源码篇 Vue 的扩展机制设计》中展开分析敬请期待5. Vue.use用法用于安装插件。插件只能注册一次多次调用无效Vue.use 必须在实例化 Vue 之前调用。Vue2 示例Vue.use(MyPlugin);Vue3示例const app Vue.createApp(App); app.use(MyPlugin);如果插件需要配置项Vue.use(MyPlugin, { someOption: true });而插件本质上是一个带有 install 方法的对象或函数const MyPlugin { install(Vue, options) { // 注册全局组件 Vue.component(MyGlobalComponent, { /* ... */ }) // 添加全局方法 Vue.prototype.$myMethod function () { /* ... */ } // 添加全局混入 Vue.mixin({ /* ... */ }) } }或者function MyPlugin(Vue, options) { // 同样逻辑 }源码分析该代码片段源码位置src/core/global-api/use.ts此处只展示相关的代码部分Vue.use function (plugin: Function | any) { const installedPlugins this._installedPlugins || (this._installedPlugins []) if (installedPlugins.indexOf(plugin) -1) { return this } const args toArray(arguments, 1) args.unshift(this) if (isFunction(plugin.install)) { plugin.install.apply(plugin, args) } else if (isFunction(plugin)) { plugin.apply(null, args) } installedPlugins.push(plugin) return this }这段代码是 Vue 2 中 Vue.use 的内部实现核心逻辑是注册插件并防止重复安装。const installedPlugins this._installedPlugins || (this._installedPlugins []) if (installedPlugins.indexOf(plugin) -1) { return this }缓存已安装的插件防止重复安装。this 指的是全局 Vue 构造函数。_installedPlugins 用来记录已经安装过的插件如果没有就初始化为空数组。如果插件已安装过直接返回。const args toArray(arguments, 1) args.unshift(this)Vue.use(plugin, ...options) 是支持传递参数的这里主要在获取到传入的其余参数例如Vue.use(MyPlugin, { foo: 1 }, extraValue)plugin 是第一个参数后面的 { foo: 1 } 和 extraValue 是传给插件的参数。从 arguments 中取出index 1及之后的所有参数组成一个新的数组。例如function installPlugin(...args) { console.log(args) } Vue.use(MyPlugin, param1, param2)arguments [MyPlugin, param1, param2]toArray(arguments, 1) 得到的是[param1, param2]用 unshift 把 this即 Vue 构造函数放到第一个参数形如 [Vue, ...options]供插件使用。if (isFunction(plugin.install)) { plugin.install.apply(plugin, args) } else if (isFunction(plugin)) { plugin.apply(null, args) }判断插件的类型并调用其 install 方法或本体Vue.use 会分别调用plugin.install(Vue, options)或 plugin(Vue, options)installedPlugins.push(plugin)记录当前插件已安装6. Vue.observable用法用于创建响应式对象。返回的对象可以直接用于渲染函数和计算属性内并且会在发生改变时触发相应的更新。Vue2 示例const state Vue.observable({ count: 0 }); state.count;Vue3示例const state reactive({ count: 0 }); state.count;总结它内部调用了observe方法在这篇文章中已经分析过源码源码篇 剖析 Vue2 双向绑定原理_vue源码解析双向绑定-CSDN博客7. Vue.mixin用法用于全局注册一个混入对象它会影响每个创建的组件实例。全局污染风险高注Vue2、Vue3 使用方法相同在 Vue 3 中推荐使用composition API 中的组合函数如 useXXXprovide/inject局部 mixin仍然支持插件机制下面这段代码会让每个组件在创建时都执行 created并拥有 sayHello() 方法。Vue.mixin({ created() { console.log(全局混入的 created 被调用) }, methods: { sayHello() { console.log(Hello from mixin) } } })会和组件自身的选项合并如 data, methods, created 等混入合并规则例如在组件中直接使用混入方法export default { logName: MyComponent, mounted() { this.sayHello() } }源码分析该代码片段源码位置src/core/global-api/mixin.ts此处只展示相关的代码部分export function initMixin(Vue: GlobalAPI) { Vue.mixin function (mixin: Object) { this.options mergeOptions(this.options, mixin) return this } }initMixin(Vue) 作用是初始化全局 API 的一部分例如 Vue.mixin, Vue.extend, Vue.use 等this.options mergeOptions(this.options, mixin)这是最关键的一句this 是 Vue 构造函数this.options 是全局配置对象存储全局组件、指令、生命周期等mixin 是用户传入的配置对象如data、methods、生命周期钩子等mergeOptions() 是 Vue 内部用于合并两个配置对象的方法它处理了生命周期钩子合并data、props、methods 合并组件、指令等全局配置合并递归合并、原型链继承等处理细节。最终的效果是将 mixin 中的内容注入到全局 Vue.options 中从而影响后续所有组件。8. Vue.nextTick用法一个异步方法用于在下次 DOM 更新循环结束之后执行延迟回调。注Vue2、Vue3 使用方法相同Vue.nextTick(() { console.log(DOM updated!); });在 Vue3 中可以在 async/await 中使用await Vue.nextTick()为什么需要使用它Vue 在更新数据时是异步批处理的。例如this.message hello this.$refs.box.innerText // ❌ 可能还是旧的内容在这种情况下如果马上访问 DOM可能还没更新完。所以应该这样this.message hello Vue.nextTick(() { // ✅ 现在 DOM 已经更新 console.log(this.$refs.box.innerText) })源码分析该代码片段源码位置src/core/util/next-tick.ts此处只展示相关的代码部分这里有一部分代码是js事件循环相关的此处先不说明直接看核心函数 nextTickconst callbacks: ArrayFunction [] let pending false function flushCallbacks() { pending false const copies callbacks.slice(0) callbacks.length 0 for (let i 0; i copies.length; i) { copies[i]() } } export function nextTick(cb?: (...args: any[]) any, ctx?: object) { let _resolve callbacks.push(() { if (cb) { try { cb.call(ctx) } catch (e: any) { handleError(e, ctx, nextTick) } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending true timerFunc() } if (!cb typeof Promise ! undefined) { return new Promise(resolve { _resolve resolve }) } }这段代码可以分为三部分缓存任务回调队列callbacks 数组异步调度处理函数flushCallbacks主方法 nextTick —— 把回调放进队列并在合适时机执行const callbacks: ArrayFunction [] let pending falsecallbacks 是所有通过 nextTick 注册的回调函数队列pending 用来避免重复触发异步定时器只调度一次执行。function flushCallbacks() { pending false const copies callbacks.slice(0) callbacks.length 0 for (let i 0; i copies.length; i) { copies[i]() } }把当前回调队列拷贝到 copies 中执行避免执行过程中又添加新的执行完清空原队列这是 “DOM 更新完成之后所有任务的批处理执行点”。callbacks.push(() { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, nextTick) } } else if (_resolve) { _resolve(ctx) } })将 cb 包成函数压入 callbacks 队列中如果没传 cb说明调用的是 await nextTick()这时保存 resolve 函数执行时用于 Promise 触发。if (!pending) { pending true timerFunc() }如果当前还没排队过 flushCallbacks就安排一个异步任务执行它timerFunc() 是平台相关的“微任务调度器”源码中会根据环境选择 Promise.then / MutationObserver / setTimeout 等。if (!cb typeof Promise ! undefined) { return new Promise(resolve { _resolve resolve }) }如果没有传回调如await nextTick()就返回一个 Promise当队列执行完调用 resolve()。9. Vue.set 和 Vue.delete用法注Vue.set 和 Vue.delete 这两个API 仅在 Vue2中使用。在 Vue 3 中响应式系统基于 Proxy已经原生支持动态添加和删除属性。Vue2 示例Vue.set(obj, newKey, newValue); Vue.delete(obj, key);data: { person: { name: John Doe } } methods: { addProperty() { this.$set(this.person, age, 30); }, removeProperty() { this.$delete(this.person, age); } }Vue3 示例Vue3 的响应式系统源码分析会持续更新这里不对 reactive 和 ref 展开描述。const person reactive({ name: John Doe }); function addProperty() { person.age 30; } function removeProperty() { delete person.age; }源码分析该代码片段源码位置src/core/observer/index.ts此处只展示相关的代码部分export function setT(array: T[], key: number, value: T): T export function setT(object: object, key: string | number, value: T): T export function set( target: any[] | Recordstring, any, key: any, val: any ): any { if (__DEV__ (isUndef(target) || isPrimitive(target))) { warn( Cannot set reactive property on undefined, null, or primitive value: ${target} ) } if (isReadonly(target)) { __DEV__ warn(Set operation on key ${key} failed: target is readonly.) return } const ob (target as any).__ob__ if (isArray(target) isValidArrayIndex(key)) { target.length Math.max(target.length, key) target.splice(key, 1, val) if (ob !ob.shallow ob.mock) { observe(val, false, true) } return val } if (key in target !(key in Object.prototype)) { target[key] val return val } if ((target as any)._isVue || (ob ob.vmCount)) { __DEV__ warn( Avoid adding reactive properties to a Vue instance or its root $data at runtime - declare it upfront in the data option. ) return val } if (!ob) { target[key] val return val } defineReactive(ob.value, key, val, undefined, ob.shallow, ob.mock) if (__DEV__) { ob.dep.notify({ type: TriggerOpTypes.ADD, target: target, key, newValue: val, oldValue: undefined }) } else { ob.dep.notify() } return val }开头两行声明了重载函数 set该函数可以用于设置数组或对象的值。export function setT(array: T[], key: number, value: T): T export function setT(object: object, key: string | number, value: T): Texport function set( target: any[] | Recordstring, any, key: any, val: any ): any {这里是函数的主体接受三个参数target目标对象或数组。key要设置的属性或数组索引。val要设置的值。if (__DEV__ (isUndef(target) || isPrimitive(target))) { warn( Cannot set reactive property on undefined, null, or primitive value: ${target} ) }检查 target 是否是 undefined 或原始类型null、boolean、number、string、symbol。如果是会在开发模式下警告表示无法在这些类型上设置响应式属性。if (isReadonly(target)) { __DEV__ warn(Set operation on key ${key} failed: target is readonly.) return }检查 target 是否为只读对象。如果是会在开发模式下发出警告表示不能在只读对象上设置属性并直接返回。const ob (target as any).__ob__ob 是目标对象的响应式代理对象__ob__ 是 Vue 用来追踪对象的响应式代理对象。每个响应式对象都会有一个 _ob_ 属性指向它的代理对象。if (isArray(target) isValidArrayIndex(key)) { target.length Math.max(target.length, key) target.splice(key, 1, val) if (ob !ob.shallow ob.mock) { observe(val, false, true) } return val }如果 target 是一个数组且 key 是有效的数组索引时的处理。if (key in target !(key in Object.prototype)) { target[key] val return val }如果 key 已经存在于 target 对象中且 key 不是 Object.prototype 上的属性即它是该对象自身的属性则直接给该属性赋值。if ((target as any)._isVue || (ob ob.vmCount)) { __DEV__ warn( Avoid adding reactive properties to a Vue instance or its root $data at runtime - declare it upfront in the data option. ) return val }如果 target 是一个 Vue 实例或者该对象属于某个 Vue 实例则在开发模式下警告表示避免在 Vue 实例运行时动态添加属性应当在 data 选项中声明。if (!ob) { target[key] val return val }如果 target 没有 __ob__即它不是响应式对象则直接给它的属性赋值并返回。defineReactive(ob.value, key, val, undefined, ob.shallow, ob.mock)使用 defineReactive 方法来设置该属性的响应式特性。if (__DEV__) { ob.dep.notify({ type: TriggerOpTypes.ADD, target: target, key, newValue: val, oldValue: undefined }) } else { ob.dep.notify() }开发模式下ob.dep.notify() 会触发依赖的更新。非开发模式下调用 ob.dep.notify() 来通知所有依赖更新。下面是 delete 方法实现源码大部分代码与 set 方法一致只针对不同的地方进行说明。export function delT(array: T[], key: number): void export function del(object: object, key: string | number): void export function del(target: any[] | object, key: any) { if (__DEV__ (isUndef(target) || isPrimitive(target))) { warn( Cannot delete reactive property on undefined, null, or primitive value: ${target} ) } if (isArray(target) isValidArrayIndex(key)) { target.splice(key, 1) return } const ob (target as any).__ob__ if ((target as any)._isVue || (ob ob.vmCount)) { __DEV__ warn( Avoid deleting properties on a Vue instance or its root $data - just set it to null. ) return } if (isReadonly(target)) { __DEV__ warn(Delete operation on key ${key} failed: target is readonly.) return } if (!hasOwn(target, key)) { return } delete target[key] if (!ob) { return } if (__DEV__) { ob.dep.notify({ type: TriggerOpTypes.DELETE, target: target, key }) } else { ob.dep.notify() } }检查属性是否存在if (!hasOwn(target, key)) { return }删除并通知依赖delete target[key]11. Vue.extend用法注Vue2、Vue3 使用方法相同用于创建一个扩展的 Vue 构造器常用于定义组件。初始有一个 idapp 的 div元素div idapp/div使用 Vue.extend(options) 【{Object} options】 将展示 message 数据的 span 标签写入 div 中const Extended Vue.extend({ template: span{{ message }}/span, data() { return { message: Hello from extended component! }; } }); const instance new Extended(); instance.$mount(#app);使用 Vue.extend() 创建这个组件通过 $mount(#mount-point) 将组件实例挂载到 idapp 的元素上并将 message 插入span 标签中结果如下div idapp spanHello from extended component!/span /div源码分析该代码片段源码位置src/core/global-api/extend.ts此处只展示相关的代码部分Vue.extend function (extendOptions: any): typeof Component { extendOptions extendOptions || {} const Super this const SuperId Super.cid const cachedCtors extendOptions._Ctor || (extendOptions._Ctor {}) if (cachedCtors[SuperId]) { return cachedCtors[SuperId] } const name getComponentName(extendOptions) || getComponentName(Super.options) if (__DEV__ name) { validateComponentName(name) } const Sub function VueComponent(this: any, options: any) { this._init(options) } as unknown as typeof Component Sub.prototype Object.create(Super.prototype) Sub.prototype.constructor Sub Sub.cid cid Sub.options mergeOptions(Super.options, extendOptions) Sub[super] Super if (Sub.options.props) { initProps(Sub) } if (Sub.options.computed) { initComputed(Sub) } Sub.extend Super.extend Sub.mixin Super.mixin Sub.use Super.use ASSET_TYPES.forEach(function (type) { Sub[type] Super[type] }) if (name) { Sub.options.components[name] Sub } Sub.superOptions Super.options Sub.extendOptions extendOptions Sub.sealedOptions extend({}, Sub.options) cachedCtors[SuperId] Sub return Sub }这段代码是 Vue 中 Vue.extend() 方法的实现用于创建一个新的 Vue 组件构造器继承父组件的选项并返回一个新的构造器。 下面对代码内容进行分解说明Vue.extend function (extendOptions: any): typeof ComponentVue.extend() 方法接收一个 extendOptions 参数这个参数是扩展的组件选项如 data、computed、methods、props 等。返回的类型是新的组件构造器typeof Component即一个类或构造函数。extendOptions extendOptions || {} const Super this const SuperId Super.cid const cachedCtors extendOptions._Ctor || (extendOptions._Ctor {})确保 extendOptions 不是 undefined 或 null如果没有传入参数则使用空对象作为默认值。Super 代表当前 Vue 构造器即调用 extend 方法的 Vue 类。SuperId 存储当前构造器的 cid是 Vue 为每个构造器分配的唯一标识符。cachedCtors 用于缓存已经创建过的组件构造器。_Ctor 存储在 extendOptions 上用于缓存当前构造器。如果 extendOptions 中已经有 _Ctor则使用它如果没有则创建一个新的空对象。if (cachedCtors[SuperId]) { return cachedCtors[SuperId] }如果当前构造器已经被创建过直接返回缓存的构造器。const name getComponentName(extendOptions) || getComponentName(Super.options) if (__DEV__ name) { validateComponentName(name) }从 extendOptions 中获取组件名称如果没有则从父组件 Super.options 中获取。如果当前是开发环境并且组件有名称验证组件名称是否合法。const Sub function VueComponent(this: any, options: any) { this._init(options) } as unknown as typeof Component Sub.prototype Object.create(Super.prototype) Sub.prototype.constructor Sub Sub.cid cid创建一个新的构造函数 Sub它将作为新的组件构造器。VueComponent 是一个临时函数调用其 this._init(options) 方法来初始化组件实例。将 Super 父类的原型继承到 Sub 子类中并为 Sub 组件分配一个唯一的 cid。Sub.options mergeOptions(Super.options, extendOptions) Sub[super] Super合并父组件的选项 Super.options 和扩展选项 extendOptions将结果存储 Sub.options 中。给 Sub 添加一个 super 属性指向父组件 Super。这允许在组件内部访问父类的信息。if (Sub.options.props) { initProps(Sub) } function initProps(Comp: typeof Component) { const props Comp.options.props for (const key in props) { proxy(Comp.prototype, _props, key) } }如果 Sub.options 中定义了 props则调用 initProps 方法来初始化 propsif (Sub.options.computed) { initComputed(Sub) } function initComputed(Comp: typeof Component) { const computed Comp.options.computed for (const key in computed) { defineComputed(Comp.prototype, key, computed[key]) } }如果 Sub.options 中定义了 computed则调用 initComputed 方法来初始化计算属性。Sub.extend Super.extend Sub.mixin Super.mixin Sub.use Super.use ASSET_TYPES.forEach(function (type) { Sub[type] Super[type] }) if (name) { Sub.options.components[name] Sub }Sub 继承父类的一些方法。遍历 ASSET_TYPES将 Super 上的相应类型的资源复制到 Sub 上。如果组件有名称将 Sub 作为组件注册到 Sub.options.components 中确保它能在父组件中被使用。Sub.superOptions Super.options Sub.extendOptions extendOptions Sub.sealedOptions extend({}, Sub.options) cachedCtors[SuperId] Sub return Sub给 Sub 新增三个独有的属性。将新创建的组件构造器 Sub 缓存到 cachedCtors 中便于复用。返回创建的新组件构造器 Sub。总结将整个过程看作一个 类扩展 的过程Vue.extend() 通过原型继承基础类Vue并添加一些新的属性和方法来生成一个新的子类Sub这个子类就是 Vue 组件的构造器能够被实例化为 Vue 组件并且具有 Vue 组件所需的所有功能。12.Vue.version用法用于获取当前 Vue 的版本。console.log(Vue.version);13. Vue.createApp()用法注Vue 2 没有这个 API它是通过 new Vue() 来创建 Vue 实例new Vue({ el: #app, data: { message: Hello, Vue 2! } });Vue.createApp() 用于创建 Vue 应用实例是 Vue3 中的 API。const app Vue.createApp({ data() { return { message: Hello, Vue 3! }; } }); app.mount(#app);源码分析因为我们下载的 Vue 源码版本是2.7因此这里面没有封装 Vue.createApp() 这个 API 的源码它的大致逻辑是这样的就不对此段代码进行展开说明了function createApp(rootComponent) { // 创建应用实例 const app { _component: rootComponent, // 存储根组件 _props: null, // 存储 props默认值为 null _container: null, // 存储挂载容器的引用 // 注册组件 component(name, component) { // 可以注册全局组件 this._components[name] component; return this; }, // 挂载到 DOM mount(container) { this._container document.querySelector(container); // 获取 DOM 容器 if (!this._container) { throw new Error(Container not found); } // 初始化根组件 const componentInstance this._createComponentInstance(this._component); this._renderComponent(componentInstance); return this; }, // 创建组件实例 _createComponentInstance(component) { // 实际实现中会有更多逻辑创建组件实例初始化其数据、生命周期等 return { component, data: component.data ? component.data() : {}, template: component.template, }; }, // 渲染组件 _renderComponent(componentInstance) { const { data, template } componentInstance; // 使用模板渲染组件这里可以是一个复杂的渲染过程 const renderedTemplate template.replace(/\{\{(\w)\}\}/g, (_, key) data[key]); // 将渲染后的结果添加到挂载容器中 this._container.innerHTML renderedTemplate; }, }; app._components {}; // 用于存储注册的全局组件 return app; }总结Vue 3 引入 createApp() 来替代 new Vue()并且一些 API 如 Vue.filter() 和 Vue.compile() 被移除或替代。Vue 3 中的响应式系统得到增强Vue.observable() 被 reactive() 和 ref() 替代Vue.set() 和 Vue.delete() 不再需要直接操作对象即可。