文章首发于集体博客 小灰灰的空间

规范化选项

new Vue 创立 Vue 实例进行初始化时,最先进行的操作就是合并选项。在合并选项时,对于不同的场景有不同的合并策略

if (options && options._isComponent) {  // 在初始化子组件时,合并子组件的 options  initInternalComponent(vm, options)} else {  // 内部调用 new Vue 时合并 options  vm.$options = mergeOptions(    resolveConstructorOptions(vm.constructor),    options || {},    vm  )}

能够看到,在创立 Vue 实例是,调用 mergeOptions 来合并选项。而在合并选项过程中,做的第一件事就是规范化选项

/** * 别离对 props inject directives 进行规范化 */normalizeProps(child, vm)normalizeInject(child, vm)normalizeDirectives(child)

上面来看下下面三个函数的具体实现

规范化 props

依照 Vue 应用文档中阐明,在定义 props 时有两种格局,别离是应用数组和对象,先来看下这两种格局的应用

// 以数组的模式列出 propsprops: ['name', 'age']// 以对象模式列出 props,以对象的模式列出 props 可指定每个 prop 的数据类型props: {  name: string,  age: age,  address: object}

既然 props 存在两种定义格局,那么在应用必然会不不便,因而须要对 props 进行规范化,将其对立成对象格局。当初来看一下 normalizeProps 的实现

function normalizeProps (options: Object, vm: ?Component) {  const props = options.props  if (!props) return  const res = {}  let i, val, name  if (Array.isArray(props)) {    // 应用数组格局列出 props    i = props.length    while (i--) {      val = props[i]      if (typeof val === 'string') {        // 数组中的每一项必须是字符串,将每一项存储到一个对象,应用每一项的指最为对象的 key, 对应的 value 为 { type: null }        name = camelize(val)        res[name] = { type: null }      } else if (process.env.NODE_ENV !== 'production') {        warn('props must be strings when using array syntax.')      }    }  } else if (isPlainObject(props)) {    // 如果应用对象格局列出 props, isPlainObject 判断一个值是否为一般对象    for (const key in props) {      val = props[key]      // 将对象的 key 转换成小驼峰格局      name = camelize(key)      // 应用三元表达式求值      res[name] = isPlainObject(val)        ? val        : { type: val }    }  } else if (process.env.NODE_ENV !== 'production') {    warn(      `Invalid value for option "props": expected an Array or an Object, ` +      `but got ${toRawType(props)}.`,      vm    )  }  options.props = res}

通过下面代码的剖析,最终能够总结出 props 对于不同写法规范化的后果

对于数组格局

props: ['name', 'age']

最终转换成上面这样

props: {  name: {    type: null  },  age: {    type: null  }}

对于对象格局

props: {  name: string,  age: number,  address: {    type: object   }}

最终转换成上面这样

props: {  name: {    type: string  },  age: {    type: number  },  address: {    type: object  }}

规范化 inject

inject 在 Vue 不能独自应用,须要与 provide 配合应用。先来看看 Vue 对 provide/inject 的解释

以容许一个先人组件向其所有子孙后代注入一个依赖,不管组件档次有多深,并在其上下游关系成立的工夫里始终失效

inject 选项应该是:

  • 一个字符串数组,或
  • 一个对象,对象的 key 是本地的绑定名,value 是:

    • 在可用的注入内容中搜寻用的 key (字符串或 Symbol),或
    • 一个对象,该对象的:

      • from property 是在可用的注入内容中搜寻用的 key (字符串或 Symbol)
      • default property 是降级状况下应用的 value

当初来看看 normalizeInject 的实现。 inject 选项有两种写法,数组的形式和对象的形式。和 props 的规定一样,最终会转换成对象格局

function normalizeInject (options: Object, vm: ?Component) {  const inject = options.inject  if (!inject) return  const normalized = options.inject = {}  // 数组格局  if (Array.isArray(inject)) {    for (let i = 0; i < inject.length; i++) {      // from: 属性是在可用的注入内容中搜寻用的 key (字符串或 Symbol)      normalized[inject[i]] = { from: inject[i] }    }  } else if (isPlainObject(inject)) {    // 对象格局    for (const key in inject) {      const val = inject[key]      normalized[key] = isPlainObject(val)        ? extend({ from: key }, val)        : { from: val }    }  } else if (process.env.NODE_ENV !== 'production') {    warn(      `Invalid value for option "inject": expected an Array or an Object, ` +      `but got ${toRawType(inject)}.`,      vm    )  }}

规范化 directive

咱们先看看指令选项的用法, Vue 容许咱们自定义指令,并且它提供了五个钩子函数 bind, inserted, update, componentUpdated, unbind,具体的用法能够参考官网-自定义指令文档,而除了能够以对象的模式去定义钩子函数外,官网还提供了一种函数的简写,例如:

{  directives: {    'color-swatch': function(el, binding) {        el.style.backgroundColor = binding.value    }  }}

函数的写法会在 bind, update 钩子中触发雷同的行为,并且不关怀其余钩子。这个行为就是定义的函数。因而在对 directives 进行规范化时,针对函数的写法会将行为赋予 bind, update 钩子。

function normalizeDirectives (options: Object) {  const dirs = options.directives  if (dirs) {    for (const key in dirs) {      const def = dirs[key]      // 函数简写的形式最终也会转换成对象模式      if (typeof def === 'function') {        dirs[key] = { bind: def, update: def }      }    }  }}

函数缓存

在下面代码,有一处代码对性能进行优化,在当前的开发中值得参考学习。

在将参数转换成驼峰格局时,每次调用函数后,都会将计算得倒的进行缓存,下次在调用时,优先应用缓存中的值,而不是从新执行函数,以进步性能。这是典型的以空间换工夫的优化,也是偏函数的经典利用

export function cached<F: Function> (fn: F): F {  const cache = Object.create(null) // 创立一个空对象缓存数据  return (function cachedFn (str: string) {    const hit = cache[str]    return hit || (cache[str] = fn(str)) // 有缓存则应用缓存,没有缓存则执行函数获取后果  }: any)}const camelizeRE = /-(\w)/g// 将 a-b 模式的写法转换成驼峰模式 aB// 这里调用 cached 函数缓存转换后果export const camelize = cached((str: string): string => {  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')})

总结

下面的三个办法中,别离对 props inject directives 进行规范化,三个选项最终都会转换成对象的格局。