跳到主要内容

官方实现

2023年06月11日
柏拉文
越努力,越幸运

一、src/core/index.ts


import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
import { version } from 'v3'

initGlobalAPI(Vue)

Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$ssrContext', {
get() {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})

// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})

Vue.version = version

export default Vue

二、src/core/global-api/index.ts initGlobalAPI()


/**
* 初始化 Vue 的众多全局 API,比如:
* 默认配置:Vue.config
* 工具方法:Vue.util.xx
* Vue.set、Vue.delete、Vue.nextTick、Vue.observable
* Vue.options.components、Vue.options.directives、Vue.options.filters、Vue.options._base
* Vue.use、Vue.extend、Vue.mixin、Vue.component、Vue.directive、Vue.filter
*
*/

export function initGlobalAPI(Vue: GlobalAPI) {
// config
const configDef: Record<string, any> = {}
configDef.get = () => config
if (__DEV__) {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
Object.defineProperty(Vue, 'config', configDef)

// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on
// them unless you are aware of the risk.
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}

Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick

// 2.6 explicit observable API
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}

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

// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue

// 在 Vue.options.components 中添加内置组件,比如 keep-alive
extend(Vue.options.components, builtInComponents)

initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}

三、src/core/global-api/extend.ts initExtend()


/**
* 基于 Vue 去扩展子类,该子类同样支持进一步的扩展
* 扩展时可以传递一些默认配置,就像 Vue 也会有一些默认配置
* 默认配置如果和基类有冲突则会进行选项合并(mergeOptions)
*/

export function initExtend(Vue: GlobalAPI) {
/**
* Each instance constructor, including Vue, has a unique
* cid. This enables us to create wrapped "child
* constructors" for prototypal inheritance and cache them.
*/
Vue.cid = 0
let cid = 1

/**
* Class inheritance
*/
Vue.extend = function (extendOptions: any): typeof Component {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
/**
* 利用缓存,如果存在则直接返回缓存中的构造函数
* 什么情况下可以利用到这个缓存?
* 如果你在多次调用 Vue.extend 时使用了同一个配置项(extendOptions),这时就会启用该缓存
*/
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}

const name =
getComponentName(extendOptions) || getComponentName(Super.options)
if (__DEV__ && name) {
validateComponentName(name)
}
// 定义 Sub 构造函数,和 Vue 构造函数一样
const Sub = function VueComponent(this: any, options: any) {
// 初始化
this._init(options)
} as unknown as typeof Component
// 通过原型继承的方式继承 Vue
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
// 选项合并,合并 Vue 的配置项到 自己的配置项上来
Sub.options = mergeOptions(Super.options, extendOptions)
// 记录自己的基类
Sub['super'] = Super

// 初始化 props,将 props 配置代理到 Sub.prototype._props 对象上
// 在组件内通过 this._props 方式可以访问
if (Sub.options.props) {
initProps(Sub)
}
// 初始化 computed,将 computed 配置代理到 Sub.prototype 对象上
// 在组件内可以通过 this.computedKey 的方式访问
if (Sub.options.computed) {
initComputed(Sub)
}

// 定义 extend、mixin、use 这三个静态方法,允许在 Sub 基础上再进一步构造子类
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use

// 定义 component、filter、directive 三个静态方法
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// 递归组件的原理,如果组件设置了 name 属性,则将自己注册到自己的 components 选项中
if (name) {
Sub.options.components[name] = Sub
}

// 在扩展时保留对基类选项的引用。
// 稍后在实例化时,我们可以检查 Super 的选项是否具有更新
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)

// 缓存
cachedCtors[SuperId] = Sub
return Sub
}
}