计算属性-computed:仅会在其响应式依赖更新时才重新计算
1、模板中的表达式虽然方便,但模板中写太多逻辑,会让模板变得臃肿,难以维护。
2、computed具有缓存作用。
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
function calculateBooksMessage() {
return author.books.length > 0 ? 'Yes' : 'No'
}
</script>
<template>
// 这么写当然也可以,但是如果页面中很多地方需要使用,模板就会很臃肿
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
<span>{{ publishedBooksMessage }}</span>
// 这么写当然也可以,但是页面重新渲染的时候,方法会再次调用,而computed具有缓存作用则不会
<span>{{ calculateBooksMessage() }}</span>
</template>
侦听器-watch&watchEffect:在每次响应式状态发生变化时触发回调函数
计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态
- watch
<script setup>
import { ref, watch } from 'vue'
const question = ref('')
const obj = reactive({ count: 0 })
// 可以直接侦听一个 ref
watch(question, async (newval, oldval) => {
// 可以执行一些异步操作或者dom更新
const res = await fetch('https://yesno.wtf/api')
})
// 监听对象属性,注意第一个参数是一个方法,切记不可以直接watch obj.count,因为得到的是number,不具备响应式
watch(
() => obj.count,
(count) => {
console.log(`count is: ${count}`)
}
)
// 深度监听:对象的属性(含多层级)发生改变,由于需要遍历嵌套属性,数据结构太深开销会很大
watch(
() => state.someObject,
(newValue, oldValue) => {
// 注意:`newValue` 此处和 `oldValue` 是相等的
// *除非* state.someObject 被整个替换了
},
{ deep: true }
)
// 即时回调的侦听器:watch是惰性的,仅当数据源变化时,才会执行回调。可以通过如下配置立即执行
watch(source, (newValue, oldValue) => {
// 立即执行,且当 `source` 改变时再次执行
}, { immediate: true })
</script>
- watchEffect
我认为watchEffect可以看做成watch+immediate: true+computed触发机制的合写
// 这里不同的是,watchEffect第一个参数不需要写监听的源
// 这个例子回调会立即执行,相当于immediate: true
watchEffect(async () => {
const response = await fetch(
// 这里有点像computed:每当 todoId.value 变化时,回调会再次执行
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
})
- watch vs watchEffect
相同点:都能响应式地执行有副作用的回调
不同点:watch只追踪明确侦听的数据源,比如说我监听obj,只能监听obj的属性变化,但是watchEffect里面既可以监听obj的属性变化,也可以监听其他对象的属性变化。简单说,watch追踪更加明确,而watchEffect则不太明确。 - 回调的触发时机
当你更改了响应式状态,它可能会同时触发 Vue 组件更新和侦听器回调。默认情况下,侦听器回调的触发在DOM更新之前,这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态。如果想在侦听器回调中能访问被 Vue 更新之后的 DOM,可通过如下配置:
watch(source, callback, {
flush: 'post'
})
watchEffect(callback, {
flush: 'post'
})
// 后置刷新的 watchEffect() 有个更方便的别名 watchPostEffect()
import { watchPostEffect } from 'vue'
watchPostEffect(() => {
/* 在 Vue 更新后执行 */
})
- 停止侦听器
const unwatch = watchEffect(() => {})
// ...当该侦听器不再需要时
unwatch()