Vue源码中编程技巧记录

Posted by Yinode on Wednesday, December 19, 2018

TOC

记录下我在阅读过程,觉得非常有学习的必要,非常好的东西。

操作对象属性的引用 <性能-可读性>

exp

function name(vm) {
  let data = vm.$options.data
  // 接下来需要读取data 全部从data中读取

这个东西几乎是全程在用,可以说出场率超级高

优点

  1. 增加性能 减少一次属性的读取 直接操作引用 当然更快啦
  2. 可读性 接下来的代码你都能少写 vm.\$options

这个习惯其实我也有,忘了从哪里开始偷学的了,实际体验确实非常好。

读取赋值初始化判断套餐 <可读性>

exp

data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}

比上面的出场率略低一点,也非常常见,一行代码完成了 取值 赋值 类型判断 重新初始化

优点

  1. 非常简洁,可读性也很不错

放弃连续声明 <不确定>

exp

const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods

这个不好说到底是不是优点,从可读性上讲我觉得绝对会有一些优势,但是性能上会比连续声明弱一些。我个人是更加喜欢放弃连续声明的,不知道大家是怎么样的看法。

while 遍历对象 <可读性>

这个东西比较适合不需要明确顺序的遍历一些东西,比如说对象,如果想要从 0 到 length-1 的顺序,那么会多些不少代码,就不太划算了。

exp

let ab = { a: 1, b: 2 }

let keys = Object.keys(ab)
let i = keys.length
while (i--) {
  console.log(keys[i])
}

优点

  1. 其实优点并不是特别的确定 不过确实是一种比较优秀的遍历手段 代码量很少(其实我个人更喜欢用 forEach 遍历对象)

检测声明赋值 V2 <可读性>

适合比上面那种方式更加简单的情况

exp

const installedPlugins = this._installedPlugins || (this._installedPlugins = [])

优点

  1. 非常高度可读性 减少代码量

高度可读性的变量 <可读性>

Vue 的源码中大量利用了非常高可读性的通用变量。

由于 JS 不支持命名参数,所以很多时候你必须要把参数传完整,但是有些时候又不要传递某个参数,比如在传递回调函数的时候,有些时候你并不需要使用回调。在 Vue 的源码中使用了这样的手段

exp

new Watcher(
  vm,
  updateComponent,
  noop,
  {
    before() {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate')
      }
    }
  },
  true /* isRenderWatcher */
)

这里的 noop 就是传递回调函数,而这个用例并不要回调,所以传递了一个空函数的变量,并且取名为 noop,这样在 Watcher 的构造函数中,就不要写正相反查,直接运行就完事了,并且可读性也很好

除此之外 空对象 等等等 很多东西都可以用这种方式来书写,统一管理,高可读性。

变量重复利用 <性能>

exp

let ancestor = vnode
while (ancestor) {
  if (isDef((i = ancestor.context)) && isDef((i = i.$options._scopeId))) {
    nodeOps.setStyleScope(vnode.elm, i)
  }
  ancestor = ancestor.parent
}

这东西其实可以转换为一个比较普通的正相反查,只不过他用了非常巧妙的指向技巧 不断深入判断 最后还把这个 I 给用上了 牛逼啊 这个技巧其实出场率也非常高,把一些本来会废弃的变量重新进行利用,会损失一定的可读性,但是可以获得更强的性能。减少变量的声明。

合理利用公共变量

exp

const activeInstance: any = null
Vue.prototype._update = function(vnode: VNode, hydrating?: boolean) {
  const prevActiveInstance = activeInstance
  // 这玩意是个全局变量
  activeInstance = vm
}
// 接下来其实在整个生命周期 以及子组件的渲染用 都回访问这个当前正在update的变量

这个东西我不知道怎么点评,因为实质上他带给我一些困惑 我找这个变量的起源找了一些时间

快速转 Boolean

exp

needRuntime = !!gen(el, dir, state.warn)

非常好用,几乎没有副作用

帮助函数

exp

export function isUndef(v: any): boolean %checks {
  return v === undefined || v === null
}

export function isDef(v: any): boolean %checks {
  return v !== undefined && v !== null
}

export function isTrue(v: any): boolean %checks {
  return v === true
}

export function isFalse(v: any): boolean %checks {
  return v === false
}

更好的代码阅读体验。

不写匿名函数

exp

Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: function reactiveGetter() {
    const value = getter ? getter.call(obj) : val
    if (Dep.target) {
      dep.depend()
      if (childOb) {
        childOb.dep.depend()
        if (Array.isArray(value)) {
          dependArray(value)
        }
      }
    }
    return value
  },
  set: function reactiveSetter(newVal) {
    const value = getter ? getter.call(obj) : val
    /* eslint-disable no-self-compare */
    if (newVal === value || (newVal !== newVal && value !== value)) {
      return
    }
    /* eslint-enable no-self-compare */
    if (process.env.NODE_ENV !== 'production' && customSetter) {
      customSetter()
    }
    // #7981: for accessor properties without setter
    if (getter && !setter) return
    if (setter) {
      setter.call(obj, newVal)
    } else {
      val = newVal
    }
    childOb = !shallow && observe(newVal)
    dep.notify()
  }
})

这个可以帮助 debug 进行堆栈跟踪,而不会是一个莫名其妙的没名字的匿名函数