本系列
Vue2中这些全局Api很重要,谈到源码大家都关注响应原理、模板编译、更新策略这些点,但是Vue在第一步做的事情是注册全局Api,没有全局Api就没有后面的一切
全局Api都在initGlobalAPI函数中初始化
function initGlobalAPI (Vue: GlobalAPI) {
// config
const configDef = {}
configDef.get = () => config
if (process.env.NODE_ENV !== 'production') {
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
extend(Vue.options.components, builtInComponents)
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}
Vue.util提供的工具方法
Vue.util.mergeOptions 函数合并对象
这个方法粗看像是 lodash 中的merge方法或者 Object.assign 方法,其实不止这么简单,并不是简单的对象深浅拷贝合并。
在lodash 中的merge方法或者 Object.assign 方法中处理合并,相同key是用后一个覆盖前一个。
但是对于Vue中,参数对象可以包含 props,data,computd,methods,inject,provide,watch,component,directive,filter,created生命周期等等属性,就不能是直接覆盖,应该是根据不同属性key,采取不同的合并策略,这就关联上vue2中值得关注的全局Api之config对象文中讲到的 config.optionMergeStrategies 参数了
function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (process.env.NODE_ENV !== 'production') {
checkComponents(child)
}
if (typeof child === 'function') {
child = child.options
}
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
// Apply extends and mixins on the child options,
// but only if it is a raw options object that isn't
// the result of another mergeOptions call.
// Only merged options has the _base property.
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
}
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
Vue.util.defineReactive 方法 、 Vue.set 方法 、 Vue.observable 方法区别
我们在头部 initGlobalAPI 看到,全局提供了 Vue.util.defineReactive 方法 、 Vue.set 方法 、 Vue.observable 方法,这三个方法都是提供响应式的,为什么要写三个呢,当然 Vue.set 方法 、 Vue.observable 方法 最终都是调用的 Vue.util.defineReactive 这一个方法,具体有什么区别呢?
核心部分 defineReactive 代码
defineReactive 中:
-
用于给对象设置新属性
-
obj对象可以是响应式对象,也可不是响应式对象,这里没有要求,但是会
observe(val)把val做成响应式
function defineReactive (
obj: Object,
key: string,
val: any,
) {
const dep = new Dep()
let childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
}
return value
},
set: function reactiveSetter (newVal) {
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
核心部分 set 代码
set 比 defineReactive 更严格,其中:
-
用于给对象设置新属性
-
target 对象必须是响应式对象
-
再调用 defineReactive 方法
function set (target: Array<any> | Object, key: any, val: any): any {
const ob = (target: any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && 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)
ob.dep.notify()
return val
}
核心部分 observe 代码
- 用于给对象做响应式,不同于set和defineReactive是给属性设置响应式
function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}