zoukankan      html  css  js  c++  java
  • vue computed 实现原理与 watch 对比

    原理

    <div id="app">
      {{num}}
      {{num1}}
    </div>
    
    var app = new Vue({
      el: '#app',
      data: {
        num: 1
      },
      computed: {
        num1 () {
          return this.num + 1;
        }
      }
    });
    console.log(app);
    

    computed 本质是一个惰性求值的订阅者。data 属性的 Observer 挂在 _data 属性下,而 computed 属性挂在 _computedWatchers 下。而发布者 Dep 里存放了两个订阅者,而和computed相关的订阅者,其实只做了一件事情,标记 dirty 为 true,等待 get 时再真正计算。

    computed 内部实现了一个惰性的 watcher,也就是 _computedWatchers,_computedWatchers 不会立刻求值,同时持有一个 dep 实例。

    其内部通过 this.dirty 属性标记计算属性是否需要重新求值

    变更流程如下:

    1. 当 computed 的依赖的状态发生改变时,就会通知这个惰性的 watcher。
    2. computed watcher 通过 this.dep.subs.length 判断有没有订阅者,
      1. 有的话,会重新计算,然后对比新旧值,如果变化了,会重新渲染。 (Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 重新渲染,本质上是一种优化。)
      2. 没有的话,仅仅把 this.dirty = true。 (当计算属性依赖于其他数据时,属性并不会立即重新计算,只有之后其他地方需要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)特性。)

    watch

    这是 computed 的 watcher 观察者结构。特有的 lazy 为 true 代表 getter 时需要计算更新,注意 cb 只是一个空的函数,啥也不做,但是 expression 有值

    Watcher {
        vm: Vue {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
        deep: false
        user: false
        lazy: true
        sync: false
        before: undefined
        cb: function noop(a, b, c) {}
        id: 1
        active: true
        dirty: false
        deps: (2) [Dep, Dep]
        newDeps: []
        depIds: Set(2) {3, 6}
        newDepIds: Set(0) {}
        expression: "len() {↵        return this.message.length↵      }"
        getter: ƒ len()
        value: 2
        __proto__: Object
    }
    

    这是 watch 的 观察者结构,注意 user 为 true,expression 里没有关键内容,但是 cb 里有,更新值的时候就是判定 user 为 true 时,调用 cb 获取新值。

    Watcher {
        vm: Vue {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
        deep: false
        user: true
        lazy: false
        sync: false
        before: undefined
        cb: function message() {
            this.full = this.message.join(',');
        }
        id: 2
        active: true
        dirty: false
        deps: (2) [Dep, Dep]
        newDeps: []
        depIds: Set(2) {3, 6}
        newDepIds: Set(0) {}
        expression: "message"
        getter: ƒ (obj)
        value: (2) ["a", "c", __ob__: Observer],
        __proto__: Object
    }
    

    与 watch 有什么区别

    computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。

    watch 侦听器 : 更多的是「观察」的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。

    与 watch 运用场景的对比

    需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

    当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。

  • 相关阅读:
    H3C-路由器密码恢复
    H3C-交换机密码恢复
    H3C-端口镜像
    [洛谷P4234]最小差值生成树
    [BZOJ4003]城池攻占
    [BZOJ1058]报表统计
    [BZOJ1584]Cleaning Up 打扫卫生
    [BZOJ3733]Iloczyn
    [HDU5709]Claris Loves Painting
    [BZOJ5109]大吉大利,晚上吃鸡!
  • 原文地址:https://www.cnblogs.com/everlose/p/12542011.html
Copyright © 2011-2022 走看看