源码篇 使用及分析 Vue 全局 API
2026/6/9 5:12:54 网站建设 项目流程

电梯

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 等数据形成AST
const 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: '<div>Hello 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 } }

上面我们已经分别分析了内部实现原理,下面是完整实现代码:

源码位置1:src/shared/constants.ts

export const ASSET_TYPES = ['component', 'directive', 'filter'] as const

源码位置2:src/core/global-api/index.ts

Vue.options = Object.create(null) ASSET_TYPES.forEach(type => { Vue.options[type + 's'] = Object.create(null) })

源码位置3:src/core/global-api/assets.ts

ASSET_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 中的组合函数(如 useXXX)
  • provide/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事件循环相关的,此处先不说明,直接看核心函数 nextTick:

const callbacks: Array<Function> = [] 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: Array<Function> = [] let pending = false
  • callbacks 是所有通过 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 set<T>(array: T[], key: number, value: T): T export function set<T>(object: object, key: string | number, value: T): T export function set( target: any[] | Record<string, 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 set<T>(array: T[], key: number, value: T): T export function set<T>(object: object, key: string | number, value: T): T
export function set( target: any[] | Record<string, 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 del<T>(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 构造器,常用于定义组件。初始有一个 id="app " 的 div元素:

<div id="app"></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') 将组件实例挂载到 id="app" 的元素上,并将 message 插入span 标签中,结果如下:

<div id="app"> <span>Hello 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 Component
  • Vue.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 方法来初始化 props
if (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() 不再需要,直接操作对象即可。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询