Vue—关于computed(在计算属性中return函数的问题)

8,593 阅读3分钟

一、背景

项目中偶然遇到有人在computed中这样写代码:

computed: {
  getName() {
    return function () {
      return "张三";
    };
  },
},

这段代码看似没什么毛病,实际上它违背了computed的设计初衷,computed设计的原因是为了简化模板中又长又臭的计算逻辑,使模板代码看上去更加简洁,容易维护,并且计算属性会基于响应式依赖进行缓存,从而优化性能。

二、计算属性介绍

关于计算属性官方的描述如下:

1629439771.png

文档地址: cn.vuejs.org/v2/guide/co…

为什么说上面这段代码违背了computed的设计初衷呢?

先来看看computed和methods的区别,然后我们就可以解释这个问题了,官方描述如下:

1629439867.png

文档:cn.vuejs.org/v2/guide/co…

也就是说虽然它们俩的效果相同,但computed会基于响应式依赖进行缓存,methods不会,我们来验证一下:

三、实践检验

首先创建computed和methods,打印日志,然后在页面上分别调用几次

<template>
  <div>
    <p>{{ computed_getName }}</p>
    <p>{{ computed_getName }}</p>
    <p>{{ computed_getName }}</p>
    <p>{{ computed_getName }}</p>

    <p>{{ method_getName() }}</p>
    <p>{{ method_getName() }}</p>
    <p>{{ method_getName() }}</p>
    <p>{{ method_getName() }}</p>
  </div>
</template>

<script>
export default {
  computed: {
    computed_getName() {
      console.log("computed计算属性被调用了");
      return "张三";
    },
  },
  methods: {
    method_getName() {
      console.log("methods方法被调用了");
      return "李四";
    },
  },
};
</script>

页面运行结果如下:

1629440306.png

控制台输出结果如下:

1629440328.png

结果可以看到虽然计算属性computed_getName与方法method_getName都在模板中引用了4次,但是computed_getName只被触发了1次,而method_getName方法被触发了4次,也就验证了computed具有缓存功能的特性。

如果将计算属性改一下,换成开头所说的return函数呢?

<template>
  <div>
    <!-- 相应的调用计算属性也要改成函数的形式 -->
    <p>{{ computed_getName() }}</p>
    <p>{{ computed_getName() }}</p>
    <p>{{ computed_getName() }}</p>
    <p>{{ computed_getName() }}</p>

    <p>{{ method_getName() }}</p>
    <p>{{ method_getName() }}</p>
    <p>{{ method_getName() }}</p>
    <p>{{ method_getName() }}</p>
  </div>
</template>

<script>
export default {
  computed: {
    computed_getName() {
      // 修改成return函数的形式
      return function () {
        console.log("computed计算属性被调用了");
        return "张三";
      };
    },
  },
  methods: {
    method_getName() {
      console.log("methods方法被调用了");
      return "李四";
    },
  },
};
</script>

页面运行结果如下:

1629440306.png

控制台输出结果如下:

1629440493.png

结果很明显,return函数之后,想要拿到计算结果必须要调用这个计算函数(等同于调用方法,只不过定义方法的位置放在了computed中),也就失去了缓存的作用,再来品Vue官方文档:

我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

那么,假设本例中的getName是一个开销较大的计算属性,现在它又失去了缓存的作用,我们是不是可以遵循官方约定使用方法来代替呢?

再有另一点原因是,一个计算属性的创建步骤比较复杂,有兴趣可参考如下文章进行学习(比较懒,有现成的就贴这了,轻点喷...):

cloud.tencent.com/developer/a…

而一个方法的创建就比较简单了,源码如下:

1629440642.png

去掉这一大堆的容错判断,实际上就只是遍历所有的methods然后挂载到实例vm上,如下:

function initMethods (vm, methods) {
  var props = vm.$options.props;
  for (var key in methods) {
    // 省略其他代码
    vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);
  }
}

有的时候计算需要依赖模板数据中的某个状态,你们想通过计算属性中返回一个函数进行传参,既能借助computed缓存的特性来优化内存使用,又能简化模板中大量的计算逻辑,但想法虽好,还需理性认识computed。

因此,如果你还看到有人在computed中return函数的骚操作,你知道该怎么做了吗?

四、总结

  • 计算属性适合简化模板逻辑,具备缓存特性,用得好绝对是神器
  • 在计算属性中return函数的作用与method是一致的,但是性能开销远远高于method