vue中计算属性(computed)和方法(methods)的对比

Vue干货 小白 暂无评论

目前工作中,很多公司都把vue作为自己的前端框架,vue的开发者和研究者也越来越多;不知道有多少人在研究使用vue的时候,对于computed和methods到底有什么区别处于模棱两可的状态,因为我们发现,想要实现一个需求,我们使用两种方式中的任何一个,基本上都可以实现,那么我们平时应该用什么更好呢?

首先我们先来看一下需求:

1514265575927.png

我们做一个购物车计算价格的功能,当用户自己修改数量的时候,后面的合计价格会跟着自动的改变,样式大家自己回去实现即可,我们主要看一下HTML结构和VUE的实现部分


<div id="app">
    <ul class="box">
        <li class="clearfix">
            <div class="product">
                <img v-bind:src="productData.shopImg" alt="">
                <div>
                    <span>{{productData['title']}}</span>
                    <span>单价:¥{{productData['price']}}</span>
                </div>
            </div>
            <div class="number">
                <input type="text" v-model="count">
                <span>合计价格:</span>
                <span>¥{{met_sum()}}</span><!--使用methods中的方法实现-->
                <span>¥{{com_sum}}</span><!--使用computed中的计算属性-->
            </div>
        </li>
    </ul>
</div>
<script src='node_modules/vue/dist/vue.min.js'></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            //=>记录产品的基本信息
            productData: {
                title: "Vue从入门到实战",
                price: 800,
                shopImg: 'img/vue.png'
            },
            //=>记录产品的购买数量
            count: 1
        },
        methods: {
            met_sum(){
                return (this.count * this.productData.price) || 0;
            }
        },
        computed: {
            com_sum(){
                return (this.count * this.productData.price) || 0;
            }
        }
    });
</script>

从上面的代码中,我们发现,不管是定义在methods中的met_sum,还是定义在computed中的com_sum,实现的代码一样,最后呈现出的效果也是一样的,但是不管是从官方文档还是其他文献中,我们都了解到,当前这种需求,我们最好使用computed计算属性来完成,既然代码都一样,那么使用computed到底好在哪?

vue官方文档给我们的答案
https://cn.vuejs.org/v2/guide/computed.html

计算属性是基于它们的依赖进行缓存的,计算属性只有在它的相关依赖发生改变时才会重新求值;而methods中定义的方法,每当触发重新渲染时,方法总会再次执行;

所以,如果我们需要遍历一个特别大的数组进行大量计算的时候,使用methods中定义的方法,开销的内存较大,性能不好,而计算属性可以有效的避免这个问题;

不知道你们读完这段话后,理解了多少,反正我是一脸懵逼(可能我太笨吧),为了彻底搞明白这个,我自己翻阅了很多的文档(大部分都是抄袭官网文档的),做了很多测试,终于把他搞懂了,接下来,我分享一下自己对他的理解。


自己对于原生JS掌握的还是不错的,既然当我们 new Vue({...})的时候,传递的配置项,大部分都会挂载到它的实例上,那么我把实例输出一下,看看能不能找到一些灵感。

56ad8dd8f9e212bc0dca24bb3324a094.png


57d931c88e30ad8faf8036dbad51c43f.png

不看不知道,一看吓一跳,我发现,写在methods中的met_sum是一个方法,但是写在computed中的com_sum变为了一个属性,而且这个属性是实现了get和set的(也就具备了双向数据交互的特点)

这样也就印证了,为啥在HTML结构中{{met_sum()}}需要加括号执行,而{{com_sum}}执行的时候不需要加括号(一但加括号了,就会报com_sum不是一个函数的错误)

研究到这,我更加懵逼了(看来会原生JS,有时候会让自己死的更惨啊 o(╥﹏╥)o),既然已经死透了,还有什么可怕的,看不明白,没啥,老子去看vue源码(没啥是看一遍源码解决不了的,如果有,那么再看一遍)(^_^)

VUE部分源码

var computedWatcherOptions = { lazy: true };function initComputed (vm, computed) {  // $flow-disable-line
  var watchers = vm._computedWatchers = Object.create(null);  // computed properties are just getters during SSR
  var isSSR = isServerRendering();  for (var key in computed) {    var userDef = computed[key];    var getter = typeof userDef === 'function' ? userDef : userDef.get;    if ("development" !== 'production' && getter == null) {
      warn(
        ("Getter is missing for computed property \"" + key + "\"."),
        vm
      );
    }    if (!isSSR) {      // create internal watcher for the computed property.
      watchers[key] = new Watcher(
        vm,
        getter || noop,
        noop,
        computedWatcherOptions
      );
    }    // component-defined computed properties are already defined on the
    // component prototype. We only need to define computed properties defined
    // at instantiation here.
    if (!(key in vm)) {
      defineComputed(vm, key, userDef);
    } else {      if (key in vm.$data) {
        warn(("The computed property \"" + key + "\" is already defined in data."), vm);
      } else if (vm.$options.props && key in vm.$options.props) {
        warn(("The computed property \"" + key + "\" is already defined as a prop."), vm);
      }
    }
  }
}function defineComputed (
  target,
  key,
  userDef
) {  var shouldCache = !isServerRendering();  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache
      ? createComputedGetter(key)
      : userDef;
    sharedPropertyDefinition.set = noop;
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? shouldCache && userDef.cache !== false
        ? createComputedGetter(key)
        : userDef.get
      : noop;
    sharedPropertyDefinition.set = userDef.set
      ? userDef.set
      : noop;
  }  if ("development" !== 'production' &&
      sharedPropertyDefinition.set === noop) {
    sharedPropertyDefinition.set = function () {
      warn(
        ("Computed property \"" + key + "\" was assigned to but it has no setter."),        this
      );
    };
  }  Object.defineProperty(target, key, sharedPropertyDefinition);
}function createComputedGetter (key) {  return function computedGetter () {    var watcher = this._computedWatchers && this._computedWatchers[key];    if (watcher) {      if (watcher.dirty) {
        watcher.evaluate();
      }      if (Dep.target) {
        watcher.depend();
      }      return watcher.value
    }
  }
}

经过源码分析,我算是彻底理解了官网所谓的基于依赖进行缓存是啥玩应了…(O(∩_∩)O哈哈~)

当我们在computed中设置一个方法后,例如:

let vm = new Vue({
    ...
    data: {
        productData: {
            title: "Vue从入门到实战",
            price: 800,
            shopImg: 'img/vue.png'
        },
        count: 1
    },
    computed: {
        com_sum(){            return (this.count * this.productData.price) || 0;
        }
    }
});

1、vue会把com_sum作为一个属性,挂载到vue的实例vm上(并且做了Object.defineProperty的get和set设定,让其具备双向数据绑定),把原有com_sum方法执行,把计算并且返回的结果赋值给vm.com_sum属性

  • 有些文档中说:computed中设置的方法名,不能和data中挂载的属性名重复,这个是完全不对的,相同也没关系,而且vue自己处理的结果就是建立一个和方法名相同的属性名(多亏自己研究一下,否则又被很多文档欺骗了)

  • 建议大家computed中方法名不要和methods中的方法名相同,否则vue以methods中设定的方法为准,computed中的方法将无效(data、filter、methods、computed…中最好都不要有重复的名字)

  • metods中设定的方法,直接挂在到实例上,没有做其它而外的处理,就是挂在了一个方法而已

2、在开始执行computed中的com_sum方法,给vm.com_sum赋值的过程中,com_sum方法中用到了this.count和this.productData.price,那么此时vue会把com_sum和这两个属性建立依赖

3、当模板重新渲染加载的时候,vue内部会监听count以及productData.price有没有发生改变,如果没有发生改变,也就是计算属性的依赖没有改变(官网是这样描述的),那么结构中的{{com_sum}}使用的依然是第一次计算好的vm.com_sum,也就是相当于建立了计算属性的缓存,并不需要重新的执行com_sum方法;

4、当模板重新渲染加载的时候,如果count或者productData.price中有一个改变了,那么vue会把computed中的com_sum方法重新的执行,并且把最新计算返回的值赋值给vm.com_sum(相当于建立了缓存,下一次可以使用这次缓存的值),页面呈现出最新计算的数据

综合以上的研究,我们可以发现,如果我们编写的这个方法处理的数据量较大,为了避免每一次重新渲染页面都要把方法重新执行一遍(写在methods中的方法就是这样的机制),防止消耗的性能变多,我们最好把这样的方法写在计算属性computed中。

上面就是我目前在研究学习vue中遇到的问题,以及自己是如何一步步的去解决的,这里需要大家对原生JS有一定的了解,想要了解更多的有关VUE方面知识或者JS方面的知识,大家可以访问:http://www.html5train.com/这里有很多前端开发知识视频(讲解的很专业也很深入);大家也可以关注本微信公众号,我后期还会分享一些自己的学习笔记,和大家共同的学习进步。

此外,如果你感觉这篇文章写的还不错,请帮忙转发哈!Thanks♪(・ω・)ノ~~


转载请注明: Vue教程中文网 - 打造国内领先的vue学习网站-vue视频,vue教程,vue学习,vue培训 » vue中计算属性(computed)和方法(methods)的对比

喜欢 ()or分享