细读Vue2.6.14 Core 源码(3): initGlobalAPI
/**
@date 2021-08-14
@description 细读Vue2.6.14 Core 源码(3): initGlobalAPI
*/
壹(序)
走完 Vue 构造函数相关,会来到 initGlobalAPI,这在第一章已经说过,这一章就阅读 initGlobalAPI 函数内部的实现。
贰(Vue.set)
Vue.set 方法与 Vue.prototype.$set 方法一样,都是 set 方法
export function set (target: Array<any> | Object, key: any, val: any): any {
// 生产环境下,target 为 undefined 或 target 是一个基本数据,则报警告
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
// target 为数组,且 key 正常
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
// 属性 key 在 target 对象或在其原型链中,且不在 Object.prototype 中
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
// __ob__ 属性在 Observer 类的 constructor中定义
const ob = (target: any).__ob__
// _isVue 在 Vue 的 _init 函数中声明,所以如果是 Vue 实例则不需要添加响应式
// vmCount 在 初始化 Data 时会自增,所以如果是 data 里面的数据,也不需要添加响应式
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
}
// target 没有 __ob__ 说明是一个非响应式对象
if (!ob) {
target[key] = val
return val
}
// 设置响应式,且通知更新
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
叁(Vue.delete)
Vue.delete 方法与 Vue.prototype.$delete 方法一样,都是 del 方法
export function del (target: Array<any> | Object, key: any) {
// 报警告
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
// 处理 target 为数组的情况
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.splice(key, 1)
return
}
// 获取 __ob__ 属性
const ob = (target: any).__ob__
// 无法删除 Vue 实例 以及 data 上的属性
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
)
return
}
// key 不在 target 中
if (!hasOwn(target, key)) {
return
}
delete target[key]
if (!ob) {
return
}
// 响应式数据,通知更新
ob.dep.notify()
}
肆(nextTick)
Vue.nextTick 方法与 Vue.prototype.$nextTick 方法类似,都是调用 nextTick 方法;
nextTick 的实现在 next-tick.js 中,全局会维护一个 callbacks 数组,用来调用传入 nextTick 的回调函数,然后维护一个变量 pending,用来控制该不该调用新的回调,以及一个 timerFunc,timerFunc的主要作用是根据环境判断使用什么方式去到下一次事件循环
export let isUsingMicroTask = false
const callbacks = []
let pending = false
// 刷新 cb 列表
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
// 清空 callbacks
callbacks.length = 0
// 调用各个 cb
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
let timerFunc
// 支持 Promise 且是原生的
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
// 不是 IE 支持 MutationObserver 且是原生的
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
let counter = 1
const observer = new MutationObserver(flushCallbacks)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// 支持 setImmediate,且是原生的
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
// 其他情况,使用 setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
// 填充 callbacks,等到 timerFunc 调用时,再调用cb
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
// 调用 timerFunc,其实就是利用 Event Loop
// 但是根据情况使用 Promise/MutationObserver/setImmediate/setTimeout
// 在此函数执行时,进入新的事件循环,再执行 cb
if (!pending) {
pending = true
timerFunc()
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
伍(initUse)
此函数用来扩展 Vue 的全局API(Vue.use)
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
// 首次调用时初始化 _installedPlugins
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
// 判定是否是已存在的 plugin
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// additional parameters
const args = toArray(arguments, 1)
args.unshift(this)
// install 相关 plugin
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
// 记录 install 过的 plugin
installedPlugins.push(plugin)
return this
}
}
陆(initMixin)
此函数用来扩展 Vue.mixin,主要就是利用 mergeOptions 方法
// initMixin:
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
// mergeOptions:
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (process.env.NODE_ENV !== 'production') {
// 验证组件名
checkComponents(child)
}
if (typeof child === 'function') {
child = child.options
}
// 标准化 props, inject, directives, 方便处理
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
// mergeOptions 处理过的对象会有 _base 属性,_base 最开始在 initGlobalAPI 函数中声明(Vue.options._base = Vue)
if (!child._base) {
// 调用 mergeOptions 处理 child 的 extents 和 mixins
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
// 遍历 parent
for (key in parent) {
mergeField(key)
}
// 遍历 child,有 parent 没有的字段则合并
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
}
柒(initExtend)
此函数用于扩展 Vue.extend,每个组件都会用到
export function initExtend (Vue: GlobalAPI) {
// 维护 cid
Vue.cid = 0
let cid = 1
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
// 利用 cid,如果当前是维护过的组件,直接 return cachedCtors[SuperId]
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
// 检查组件名
validateComponentName(name)
}
// 声明组件构造函数
const Sub = function VueComponent (options) {
// 依然调用 _init
this._init(options)
}
// 写入属性
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
// 处理 props
if (Sub.options.props) {
initProps(Sub)
}
// 处理 computed
if (Sub.options.computed) {
initComputed(Sub)
}
// 继承 Super 的 extend/mixin/use 方法
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
/**
ASSET_TYPES = [
'component',
'directive',
'filter'
]
*/
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// 维护 cachedCtors
cachedCtors[SuperId] = Sub
return Sub
}
}
捌(initAssetRegisters)
此函数用于扩展 Vue.component/directive/filter
export function initAssetRegisters (Vue: GlobalAPI) {
/**
ASSET_TYPES = [
'component',
'directive',
'filter'
]
*/
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
// 未传入 definition,则表示是获取,直接return
if (!definition) {
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && type === 'component') {
// 注册组件,检查组件名是否规范
validateComponentName(id)
}
// component
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
definition = this.options._base.extend(definition)
}
// directive
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
// filter
this.options[type + 's'][id] = definition
return definition
}
}
})
}
终(导航)
细读Vue2.6.14 Core 源码(2): After Vue
细读Vue2.6.14 Core 源码(3): initGlobalAPI
细读Vue2.6.14 Core 源码(5): initState
细读Vue2.6.14 Core 源码(6): defineReactive