返回

计算属性——手写Vue源码漫游(八)

前端

计算属性

计算属性允许我们在组件中定义依赖于其他数据的属性。当这些依赖数据发生变化时,计算属性也会自动更新。这使得我们可以非常方便地实现一些需要实时更新的数据,比如表单验证、错误信息等。

计算属性的实现原理

计算属性的实现原理其实并不复杂。它主要依靠Vue的响应式系统和watcher来实现。

  • 响应式系统:Vue的响应式系统可以自动追踪数据变化,当数据发生变化时,它会通知所有依赖于该数据的watcher。
  • watcher:watcher是一个对象,它可以观察一个数据,当这个数据发生变化时,watcher就会执行一个回调函数。

计算属性就是通过创建一个watcher来实现的。当计算属性的依赖数据发生变化时,watcher就会执行一个回调函数,这个回调函数会重新计算计算属性的值,并更新视图。

手写Vue源码中的计算属性

在手写Vue源码中,计算属性的实现主要集中在src/core/instance/state.jssrc/core/instance/computed.js这两个文件中。

src/core/instance/state.js文件中,定义了defineComputed方法,这个方法用于定义计算属性。

export function defineComputed (
  instance,
  key,
  userDef
) {
  const watcher = new Watcher(
    instance,
    userDef,
    null,
    { lazy: true }
  )
  Object.defineProperty(instance, key, {
    get: () => watcher.value,
    enumerable: true,
    configurable: true
  })
}

defineComputed方法中,首先创建了一个watcher,这个watcher观察计算属性的依赖数据。然后,在实例上定义一个属性,这个属性的getter函数是watcher的value函数。这样,当计算属性的依赖数据发生变化时,watcher就会执行value函数,重新计算计算属性的值,并更新视图。

src/core/instance/computed.js文件中,定义了ComputedWatcher类,这个类继承自Watcher类,它专门用于计算属性。

export class ComputedWatcher extends Watcher {
  constructor (
    vm,
    expOrFn,
    cb,
    options,
    isRenderWatcher
  ) {
    super(vm, expOrFn, cb, options, isRenderWatcher)
    this.lazy = true
    this.dirty = false // for lazy watchers
  }

  get value () {
    const vm = this.vm
    if (this.dirty) {
      // evaluate the computed value
      this.value = this.get()
      this.dirty = false
    }
    return this.value
  }

  depend () {
    const targets = this.dep.target
    if (targets) {
      targets.addDep(this)
    }
  }
}

ComputedWatcher类中,定义了一个value属性,这个属性是计算属性的值。当计算属性的依赖数据发生变化时,value属性就会重新计算。

总结

计算属性是Vue2的响应式系统中一个非常重要的概念,它允许我们在组件中定义依赖于其他数据的属性,当这些依赖数据发生变化时,计算属性也会自动更新。计算属性的实现原理并不复杂,它主要依靠Vue的响应式系统和watcher来实现。