Skip to content

计算属性

基础示例

javascript
const author = reactive({
  name: 'John Doe',
  books: ['Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery'],
})
html
<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>

当页面上的某个信息需要根据某个数据的具体内容来展示,模板表达式虽然允许我们进行一些简单的计算,但难免会使模板变得臃肿 特别是当这种现象需要多次使用时,这是就可以使用计算属性来简化模板表达式

javascript
import { computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: ['Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery'],
})
const hasPublishedBooks = computed(() => (author.books.length > 0 ? 'Yes' : 'No'))
html
<p>Has published books:</p>
<span>{{ hasPublishedBooks }}</span>

上面的代码中,我们定义了一个计算属性hasPublishedBooks,它会根据author.books.length的值来决定他的值 computed() 方法期望接收一个getter函数,返回值为一个计算属性refVue的计算属性会自动追踪响应式依赖,它会检测到hasPublishedBooks依赖于author.books,当author.books发生变化时,计算属性会自动更新

计算属性缓存 vs 方法

在表达式中展示数据时,使用计算属性和方法都能获得相同的效果

html
<p>{{ calculateBooksMessage() }}</p>

// 组件中 function calculateBooksMessage() { return author.books.length > 0 ? 'Yes' : 'No' }

然而不同之处在于,计算属性值会基于其依赖的响应式数据被缓存,只有在其依赖的响应式数据发生更新时才会重新计算,而使用方法来表示的话,每次调用都会执行一次代码逻辑,这也解释了,为什么下面这段代码不会更新

javascript
const now = computed(() => Date.now())

可写属性

计算属性默认只读.尝试修改时会收到运行时警告.但你也可以通过同时提供getter setter 来创建可写的计算属性

<script setup>
import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  // getter
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    // 注意:我们这里使用的是解构赋值语法
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

运行 fullName.value = 'John Doe' 时,setter 会被调用而 firstName 和 lastName 会随之更新。

获取上一个值

  • 仅3.4+版本支持 可以通过访问计算属性的getter的第一个参数来获取计算属性返回的上一个值
javascript
const count = ref(2)

// 这个计算属性在 count 的值小于或等于 3 时,将返回 count 的值。
// 当 count 的值大于等于 4 时,将会返回满足我们条件的最后一个值
// 直到 count 的值再次小于或等于 3 为止。
const alwaysSmall = computed((previous) => {
  if (count.value <= 3) {
    return count.value
  }

  return previous
})
javascript
<script setup>
  import {ref, computed} from 'vue'

  const count = ref(2)

  const alwaysSmall = computed({
  get(previous) {
  if (count.value <= 3) {
  return count.value
}

  return previous
},
  set(newValue) {
  count.value = newValue * 2
}
})
</script>

最佳实践

Getter 不应该又副作用

计算属性的getter应该只做计算而没有任何其他副作用,不要改变其他状态,不要操作DOM,不要进行异步操作等等.如果需要执行其他副作用,应该使用watch来实现

避免直接修改计算属性值

计算属性本意时返回一个派生值,每当源数据发生变化是就生成一个新的派生值.更改这个派生的值时没有意义的,这也是为什么计算属性默认是只读的