基础/计算属性

282 阅读3分钟

基础示例

<template> 
  <p>Has published books:</p>
  <span>{{ publishedBooksMessage }}</span> 
</template>

<script setup>
  import { ref, computed } from 'vue'
  
  const author = ref({ 
    name: 'John Doe', 
    books: [ 
      'Vue 2 - Advanced Guide', 
      'Vue 3 - Basic Guide', 
      'Vue 4 - The Mystery' 
    ] 
  }) 
  
  // 一个计算属性 
  const publishedBooksMessage = computed(() => { 
    return author.value.books.length > 0 ? 'Yes' : 'No' 
  })
</script>

你可以简单的把 computed 的返回值,认为是一个通过 ref() 声明的响应式数据,初始值就是传入 computed 的第一个参数的返回值。

import { ref, computed  } from 'vue'

  
const author = ref({ 
  name: 'John Doe', 
  books: [ 
    'Vue 2 - Advanced Guide', 
    'Vue 3 - Basic Guide', 
    'Vue 4 - The Mystery' 
  ] 
}) 

// 类似于计算属性,只不过 author 的值改变时,这个 author2 的值不会因为 author 发生改变而改变
function initValue() {
  return author.value.books.length > 0 ? 'Yes' : 'No'
}
const author2 = ref(initValue()) // 可以传递一个初始化函数
  
// 一个计算属性,如果 author 发生改变会重新计算
const publishedBooksMessage = computed(() => { 
  return author.value.books.length > 0 ? 'Yes' : 'No' 
})

当我们需要在其他函数中使用这个计算属性的时候,也是访问它的 value 属性

// ... 就像上面一样声明了一个 publishedBooksMessage 计算属性
function foo() {
  return publishedBooksMessage.value // 返回 'Yes' 或者 'No'
}

多个依赖项

在上面的例子中,计算属性只依赖于 author 这一个响应式数据。实际业务中可能一个计算属性会依赖多个响应式数据。

<template>
  {{ computedValue }}
</template>

<script setup>
  import { ref } from 'vue'
  
  const baz = ref(false)
  const foo = ref(true)
  const books = ref([])
  
  const computedValue = computed(() => {
    if (baz.value && foo.value && books.value.length) {
      return 'Yes'
    }
    return 'No'
  })
</script>

计算属性缓存 vs 方法

方法

在使用双大括号语法绑定模板数据,以及通过 v-bind 绑定 Attribute 的时候,我们可以传递一个函数调用

<template>
  <p>{{ getValue() }}</p>
  <!-- <p>{{ getValue }}</p> 这样是把整个函数作为纯文本渲染出来,而不是函数返回值. -->
</template>

<script>
  import { ref } from 'vue'
  
  const msg = ref('Hello World')

  function getValue() {
    return msg.value
  }
  
  // 别忘了返回出去,除非你用的是 <script setup><\/script>
  // 知道我为啥用 \ 符号转义script前面的 / 吗?
  // 浏览器解析 js 脚本从 <script> 标签开始,只要碰到 </script> 就认为解析完成了,所以不管在任何地方,不论是 console.log 里,还是注释里,只要不是真正的完成 js 代码编写,都不要出现 </script>。更多知识可以去看 《JavaScript高级程序设计》第四版,在 script 标签那部分有讲到
  // 再说一句 尽管我们转义了,打印出来的内容是没有转义符号的,这个请放心 console.log('<\/script>')
  return {
    getValue
  }
</script>

这样,我们就通过方法的形式渲染了 msg ,有同学会关心,我每次去改 msg 的值,页面上会更新吗?

大家可以打开控制台,或者 console,可以看到每次编辑输入框的内容,组件都会更新,模板上的方法都会重新调用。基础/响应式基础-ref篇 有讲DOM更新时机

然后我们再多加入一点内容

现在,我们不去修改msg了,我们去修改count的值。神奇的事情发生了,控制台一直在打印 getValue方法被调用了 ,这就有点让人不爽了,我修改count为啥去调用 getValue ,getValue 内部只和 msg 有关系,并没有使用 count。

这个时候计算属性就能完美解决这个问题,按照文档的说法:计算属性会缓存上一次的计算结果,只在内部涉及到的的响应式数据变化的时候,才会重新计算返回值。

在组件首次被渲染的时候没有缓存,可以看到控制台会打印一次。

得益于Vue的虚拟DOM,我们在某一次修改count后,msg相关的DOM没有变化,这些DOM会被重用。虽然我们的 onUpdated 生命周期函数被执行了,但不代表整个组件都会重新渲染,这也起到了一些性能优化的效果。

我们修改代码为:

这样每次修改 count,都不会引起 getValue 重新计算了。