官方实现
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/observer/index.ts set()
/**
* 通过 Vue.set 或者 this.$set 方法给 target 的指定 key 设置值 val
* 如果 target 是对象,并且 key 原本不存在,则为新 key 设置响应式,然后执行依赖通知
*/
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__
// 更新数组指定下标的元素,Vue.set(array, idx, val),通过 splice 方法实现响应式更新
if (isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
// when mocking for SSR, array methods are not hijacked
if (ob && !ob.shallow && ob.mock) {
observe(val, false, true)
}
return val
}
// 更新对象已有属性,Vue.set(obj, key, val),执行更新即可
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
// 不能向 Vue 实例或者 $data 添加动态添加响应式属性,vmCount 的用处之一,
// this.$data 的 ob.vmCount = 1,表示根组件,其它子组件的 vm.vmCount 都是 0
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 不是响应式对象,新属性会被设置,但是不会做响应式处理
if (!ob) {
target[key] = val
return val
}
// 给对象定义新属性,通过 defineReactive 方法设置响应式,并触发依赖更新
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
}