细读Vue2.6.14 Core 源码(3): initGlobalAPI

735 阅读2分钟

细读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 源码(1): 入口

细读Vue2.6.14 Core 源码(2): After Vue

细读Vue2.6.14 Core 源码(3): initGlobalAPI

细读Vue2.6.14 Core 源码(4): _init

细读Vue2.6.14 Core 源码(5): initState

细读Vue2.6.14 Core 源码(6): defineReactive

细读Vue2.6.14 Core 源码(7): $mount

细读Vue2.6.14 Core 源码(8): $createElement