vue2中值得关注的全局Api之config对象

159 阅读3分钟

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)
}

第一步config的全局配置信息

config 对象来自 share/config.js 中,这里也是使用的Object.defineProperty,因为是的config属性不可删除,不可重新定义,不可枚举,但是config对象里面属性可以任意修改

// 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)

这个config对象就是官网列出的字段,都是很有趣的,我们一一看看

image.png

config.optionMergeStrategies 自定义属性合并策略

我们调用 Vue.mixin 的时候,参数对象可以包含 props,data,computd,methods,inject,provide,watch,component,directive,filter,created生命周期等等属性,那每个属性应该怎么合并到实例上,这个就是optionMergeStrategies定义好了,因为不同属性合并策略肯定不一样

我们看一个内置的data属性合并方式,执行mergeDataOrFn内部原理是计算父子data函数的返回值,合并成新对象返回的函数

const strats = config.optionMergeStrategies

strats.data = function (
  parentVal: any,
  childVal: any,
  vm?: Component
): ?Function {
  if (!vm) {
    if (childVal && typeof childVal !== 'function') {
      process.env.NODE_ENV !== 'production' && warn(
        'The "data" option should be a function ' +
        'that returns a per-instance value in component ' +
        'definitions.',
        vm
      )

      return parentVal
    }
    return mergeDataOrFn(parentVal, childVal)
  }

  return mergeDataOrFn(parentVal, childVal, vm)
}

于是,我们可以扩展optionMergeStrategies对象,添加新属性的合并自定义策略,实现父子对象嵌套识别相关功能,比如官网的例子

Vue.config.optionMergeStrategies._my_option = function (parent, child, vm) {  
return child + 1  
}  
  
const Profile = Vue.extend({  
_my_option: 1  
})  
  
// Profile.options._my_option = 2

config.devtools 开启浏览器devtools插件

为什么开发环境能用devtools,而生产环境不能用呢,原因就在这里

devtools: process.env.NODE_ENV !== 'production'

所以之前遇到一个需求,是让生产环境也能打开devtools,一种就是设置true,那开发和生产环境都有了

config.devtools = true

更通用的做法是做油猴插件来无侵入式,油猴核心代码

// 从html中获取Vue根实例
const rootVm = document.getElementById('app').__vue__
// 获取Vue构造函数
const Vue = rootVm.$options._base
// 或者 
// Vue版本是2.6.14,取构造函数需要两次取__proto__
// 可以根据自己的Vue版本做调整,一般是只需要取一次
// const Vue = rootVm.__proto__.__proto__.constructor
// 获取全局配置信息
const config = Vue.config
// 设置devtools为true
Vue.config.devtools = true
// 启动devtool插件,这里来自源码 src\platforms\web\entry-runtime-with-compiler.js,第51行抛出事件
window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit('init', Vue)

config.errorHandler 异常钩子,sentry监控用这个来获取报错的

errorHandler 这个钩子的作用是用来获取报错的,生产环境在用户端各种问题,监控上做的一般是sentry,errorHandler,sourceMap 来定位问题,关于sourceMap问题可以查看# source-map从入门到放弃和sentry检测