基于vue2.0原理-自己实现MVVM框架之computed计算属性


一、先聊下Computed的用法

写一个最简单的小demo,展示用户的名字和年龄,代码如下:

<body>
  <div id="app">
    <input type="text" v-model="name"><br/>
    <input type="text" v-model="age"><br/>
    {{NameAge}}
  </div>
  <script>
    var vm = new MYVM({
      el: '#app',
      data: {
        name: 'James',
        age:18
      },
      computed:{
        NameAge(){
          return this.$data.name+" "+this.$data.age;
        }
      },
    })
  </script>
</body>

运行结果:

iShot_2022-07-16_17.38.21

从代码和运行效果可以看出,计算属性NameAge依赖于data的name属性和age属性。

特点:

1、计算属性是响应式的

2、依赖其它响应式属性或计算属性,当依赖的属性有变化时重新计算属性

3、计算结果有缓存,组件使用同一个计算属性,只会计算一次,提高效率

4、不支持异步

适用场景:

当一个属性受多个属性影响时就需要用到computed

例如:购物车计算价格
只要购买数量,购买价格,优惠券,折扣券等任意一个发生变化,总价都会自动跟踪变化。

二、原理分析

1、 computed 属性解析

每个 computed 属性都会生成对应的观察者(Watcher 实例),观察者存在 values 属性和 get 方法。computed 属性的 getter 函数会在 get 方法中调用,并将返回值赋值给 value。初始设置 dirty 和 lazy 的值为 true,lazy 为 true 不会立即 get 方法(懒执行),而是会在读取 computed 值时执行。

function initComputed(vm, computed) {    
		// 存放computed的观察者
    var watchers = vm._computedWatchers = Object.create(null);    
    //遍历computed属性
    for (var key in computed) {        
        //获取属性值,值可能是函数或对象
        var userDef = computed[key];        
        //当值是一个函数的时候,把函数赋值给getter;当值是对象的时候把get赋值给getter
        var getter = typeof userDef === 'function' ? userDef: userDef.get;      
      
        // 每个 computed 都创建一个 watcher
        // 创建watcher实例 用来存储计算值,判断是否需要重新计算
        watchers[key] = new Watcher(vm, getter, { 
             lazy: true 
        });        

        // 判断是否有重名的属性
        if (! (key in vm)) {
            defineComputed(vm, key, userDef);
        }
    }
}

代码中省略不需要关心的代码,在initComputed中,Vue做了这些事情:

  1. 为每一个computed建立了watcher。

  2. 收集所有computed的watcher,并绑定在Vue实例的_computedWatchers 上。

  3. defineComputed 处理每一个computed。

2、将computed属性添加到组件实例上

function defineComputed(target, key, userDef) {    
    // 设置 set 为默认值,避免 computed 并没有设置 set
    var set = function(){}      
    //  如果用户设置了set,就使用用户的set
    if (userDef.set) set = userDef.set   
    Object.defineProperty(target, key, {        
        // 包装get 函数,主要用于判断计算缓存结果是否有效
        get:createComputedGetter(key),        
        set:set

    });
}
// 重定义的getter函数
function createComputedGetter(key) {
    return function computedGetter() {
        var watcher = this._computedWatchers && this._computedWatchers[key];
        if (watcher) {
            if (watcher.dirty) {
                // true,懒执行
                watcher.evaluate(); // 执行watcher方法后设置dirty为false
            }
            if (Dep.target) {
                watcher.depend();
            }
            return watcher.value; //返回观察者的value值
        }
    };
}
  1. 使用 Object.defineProperty 为实例上computed 属性建立get、set方法。

  2. set 函数默认是空函数,如果用户设置,则使用用户设置。

  3. createComputedGetter 包装返回 get 函数。

本站声明:
1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;

2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;

3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;

4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;

5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/294371.html

(0)
上一篇 2022年12月1日
下一篇 2022年12月1日

相关推荐

发表回复

登录后才能评论