vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue computed和watch

Vue中computed和watch的区别

作者:Govi

在vue项目中我们常常需要用到computed和watch,那么我们究竟在什么场景下使用computed和watch呢?他们之间又有什么区别呢?本将给大家详细的介绍一下,需要的朋友可以参考下

前言🤞

在vue项目中我们常常需要用到computed和watch,那么我们究竟在什么场景下使用computed和watch呢?他们之间又有什么区别呢?记录一下!

computed和watch有什么区别?

相同点:(过目一下,下面还会更新)

computed

简而言之,它的作用就是自动计算我们定义在函数内的“公式”

 data() {
    return {
      num1: 1,
      num2: 2
    };
  },
  computed: {
    total() {
      return this.num1 * this.num2;
    }
  }

在这个场景下,当this.num1或者this.num2变化时,这个total的值也会随之变化,为什么呢?

## 计算属性实现:

computed是一个函数可以看出,它应该也有一个初始化函数 initComputed来对它进行初始化。

const computedWatcherOptions = { lazy: true }

// vm: 组件实例 computed 组件内的 计算属性对象
function initComputed (vm: Component, computed: Object) {
  // 遍历所有的计算属性
  for (const key in computed) {
    // 用户定义的 computed
    const userDef = computed[key]
    const getter = typeof userDef === 'function' ? userDef : userDef.get

    watchers[key] = new Watcher( 
      vm,
      getter || noop,
      noop,
      computedWatcherOptions
    )
 
  defineComputed(vm, key, userDef)
}

首先说说defineComputed函数

function computedGetter () {
    // 拿到 上述 创建的 watcher 实例
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      // 首次执行的时候 dirty 基于 lazy 所以是true
      if (watcher.dirty) {
        // 这个方法会执行一次计算
        // dirty 设置为 false
        // 这个函数执行完毕后, 当前 计算watcher就会推出
        watcher.evaluate()
      }
      // 如果当前激活的渲染watcher存在
      if (Dep.target) {
        /**
         * evaluate后求值的同时, 如果当前 渲染watcher 存在,
         * 则通知当前的收集了 计算watcher 的 dep 收集当前的 渲染watcher
         *
         *    为什么要这么做?
         * 假设这个计算属性是在模板中被使用的, 并且渲染watcher没有被对应的dep收集
         * 那派发更新的时候, 计算属性依赖的值发生改变, 而当前渲染watcher不被更新
         * 就会出现, 页面中的计算属性值没有发生改变的情况.
         *
         * 本质上计算属性所依赖的dep, 也可以看做这个属性值本身的dep实例.
         */
        watcher.depend()
      }
      return watcher.value
    }
  }

evaluate () {
    this.value = this.get()
    this.dirty = false
  }
set: function reactiveSetter (newVal) {
  const value = getter ? getter.call(obj) : val
  
  if (newVal === value || (newVal !== newVal && value !== value)) {
    return
  }

  // 通知它的订阅者更新
  dep.notify()
}


update () {
    /* istanbul ignore else */
    if (this.lazy) {
      // 假设当前 发布者 通知 值被重新 set
      // 则把 dirty 设置为 true 当computed 被使用的时候 就可以重新调用计算
      // 渲染wacher 执行完毕 堆出后, 会轮到当前的渲染watcher执行update
      // 此时就会去执行queueWatcher(this), 再重新执行 组件渲染时候
      // 会用到计算属性, 在这时因为 dirty 为 true 所以能重新求值
      // dirty就像一个阀门, 用于判断是否应该重新计算
      this.dirty = true
    }
  }


一开始dirtytrue,一旦执行了一次计算,就会设置为false,然后当它定义的函数内部依赖的值发生了变化,则这个值就会重新变为true。怎么理解?就拿上面的this.num1this.num2来说,当二者其中一个变化了,dirty的值就变为true

function computedGetter () {
    // 拿到 上述 创建的 watcher 实例
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      // 首次执行的时候 dirty 基于 lazy 所以是true
      if (watcher.dirty) {
        // 这个方法会执行一次计算
        // dirty 设置为 false
        // 这个函数执行完毕后, 当前 计算watcher就会推出
        watcher.evaluate()
      }
      // 如果当前激活的渲染watcher存在
      if (Dep.target) {
        /**
         * evaluate后求值的同时, 如果当前 渲染watcher 存在,
         * 则通知当前的收集了 计算watcher 的 dep 收集当前的 渲染watcher
         *
         *    为什么要这么做?
         * 假设这个计算属性是在模板中被使用的, 并且渲染watcher没有被对应的dep收集
         * 那派发更新的时候, 计算属性依赖的值发生改变, 而当前渲染watcher不被更新
         * 就会出现, 页面中的计算属性值没有发生改变的情况.
         *
         * 本质上计算属性所依赖的dep, 也可以看做这个属性值本身的dep实例.
         */
        watcher.depend()
      }
      return watcher.value
    }
  }

watch

watch更多充当监控者的角色

data() {
    return {
        total:99
    }
},
watch: {
    count: {
        hanlder(){
            console.log('total改变了')
        }
    }
}


// src/core/instance/state.js
function initWatch (vm: Component, watch: Object) {
  // 遍历我们定义的wathcer
  for (const key in watch) {
    const handler = watch[key]
    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else {
      createWatcher(vm, key, handler)
    }
  }
}

function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}

Vue.prototype.$watch = function (
    expOrFn: string | Function, // 这个可以是 key
    cb: any, // 待执行的函数
    options?: Object // 一些配置
  ): Function {
    const vm: Component = this
    // 创建一个 watcher 此时的 expOrFn 是监听对象
    const watcher = new Watcher(vm, expOrFn, cb, options)
    
    return function unwatchFn () {
      watcher.teardown()
    }
  }

Vue.prototype.$watch = function (
    expOrFn: string | Function, 
    cb: any,
    options?: Object
  )


if (typeof expOrFn === 'function') {
  this.getter = expOrFn
} else {
  // 如果是一个字符则转为一个 一个 getter 函数
  // 这里这么做是为了通过 this.[watcherKey] 的形式
  // 能够触发 被监听属性的 依赖收集
  this.getter = parsePath(expOrFn)
  if (!this.getter) {
    this.getter = noop
    process.env.NODE_ENV !== 'production' && warn(
      `Failed watching path: "${expOrFn}" ` +
      'Watcher only accepts simple dot-delimited paths. ' +
      'For full control, use a function instead.',
      vm
    )
  }
}
this.value = this.lazy
  ? undefined
  : this.get()

总结

说了这么多关于这两座大山的相关内容,也该来总结一下了。

相同点:

不同点:

#感谢

至此,本篇有关computed和watch属性的相关内容到此就结束啦,有什么补充的可以联系我哦!

以上就是Vue中computed和watch的区别的详细内容,更多关于Vue computed和watch的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文