详解Vue中Computed与watch的用法与区别
作者:我要充满正能量 时间:2023-07-02 17:05:24
computed
computed只接收一个getter函数
1、getter必须有返回值
2、computed返回一个只读响应式ref对象 (只读、响应式、对象)
注意:omputed只接收一个getter函数时,返回的只读对象,也就是不能修改他的返回值!
getter触发条件
1、computed返回值首次被读取时
2、getter绑定的响应式变量被修改时
<script setup>
import { ref,computed } from 'vue'
const num = ref(1)
//computed返回一个只读响应式ref对象computedNum
//computedNum是只读属性
let computedNum = computed(() => num.value + 1)
</script>
<template>
<p> num:{{ num }} </p>
<p>computedNum:{{ computedNum }}</p>
<!-- 修改响应式变量num 触发与之绑定的computed的getter-->
<button @click="num++">num++</button>
<!-- computedNum是只读属性-->
<button @click="computedNum++">computedNum++</button>
</template>
computed同时接收getter函数对象和setter函数对象
1、setter函数对象没有返回值
2、computed返回一个可读可写响应式对象
3、setter函数对象有参数,是getter的返回值,也是computed的值
4、修改computed返回值,触发setter函数对象执行,但不会真正修改computed返回值(setter内改变getter计算值就会改变computed返回值)
setter触发条件
computed返回值被修改时
实例:
<script setup>
import { ref, computed } from 'vue'
const num = ref(1)
//getter(只读)
let computedNum = computed(() => num.value + 1)
//getter和setter (可读可写)
let computedNum2 = computed({
get: () => num.value + 1,
set: (val) => {
console.log(val);
//setter中修改ref响应式变量num,将触发关联的num的getter计算
//computedNum和computedNum2的getter同时触发
num.value++
}
})
</script>
<template>
<p> num:{{ num }} </p>
<p>computedNum:{{ computedNum }}</p>
<p>computedNum2:{{ computedNum2 }}</p>
<button @click="num++">num++</button>
<!-- computedNum是只读属性,会有警告提醒 Write operation failed: computed value is readonly-->
<button @click="computedNum++">computedNum++</button>
<!-- computedNum2是可读可写属性-->
<button @click="computedNum2++">computedNum2++</button>
</template>
调试 Computed
使用范围:仅开发模式生效
computed的第二参数:带有 onTrack
和 onTrigger
选项的对象
onTrack
:getter关联的响应式数据时触发。onTrigger
:getter关联的响应式数据被修改时触发
<script setup>
import { ref, computed } from 'vue'
const num = ref(1)
let computedNum = computed(() => num.value + 1, {
onTrack: (e) => {
console.log('onTrack');
console.log(e);
},
onTrigger: (e) => {
console.log('onTrigger');
console.log(e);
}
})
</script>
<template>
<p> num:{{ num }} </p>
<p>computedNum:{{ computedNum }}</p>
<!--每次 num++将触发onTrigger -->
<button @click="num++">num++</button>
</template>
watchEffect
语法:
参数1:触发监听回调函数,回调函数可传入一个onInvalidate函数作为参数!
可选参数2:对象,包含3个可选属性flush、onTrack、onTrigger
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
1、会立即执行一次(和watch的immediate属性效果一致)
2、关联的响应式数据被修改时触发
3、会自动感知代码依赖,和watch不一样,watchEffect会主动绑定监听数据
局限性:不能监听对象(但可以监听对象的属性),只能监听类似ref基本数据类型的响应式数据
立即执行 监听基本数据类型
<script setup>
import { ref, watchEffect } from 'vue'
const num = ref(1)
//会立即执行一次
watchEffect(() => {
console.log('watchEffect');
num.value++
})
</script>
<template>
<p>num: {{ num }}</p>
<button @click="num++">num++</button>
</template>
停止watchEffect
隐式:组件卸载时自动停止
显式:调用watchEffect
返回值
const stop = watchEffect(() => {
/* ... */
})
// 显式停止
stop()
清理watchEffect
语法: watchEffect( onInvalidate=>{ onInvalidate(()=>{ }) })
onInvalidate 是一个函数!优先触发!
onInvalidate 执行时机:
1、watchEffect被重新触发时
2、组件卸载时
注意:关联的响应式数据首次被修改时不会触发onInvalidate函数!
作用: 清理定时器、事件监听removeEventListener 。。。
import { ref, watchEffect } from 'vue'
const num = ref(1)
watchEffect((onInvalidate ) => {
console.log('watchEffect-1');
num.value++
onInvalidate (()=>{
console.log('onInvalidate-2');
})
console.log('watchEffect-3');
})
//1、watchEffect 被重新触发时
// onInvalidate-2
// watchEffect-1
// watchEffect-3
//2、组件卸载时
// onInvalidate-2
//3、关联的响应式数据首次被修改(组件被挂载时)
// watchEffect-1
// watchEffect-3
watchPostEffect 和 watchSyncEffect
watchPostEffect
和 watchSyncEffect
在Vue3.2新增,是watchEffect类似语法糖的东西,
是watchEffect
可选参数对象{ flush?: 'pre' | 'post' | 'sync'}
中post和sync的语法糖,pre是默认值
推迟触发watchPostEffect
watchPostEffect
是watchEffect可选参数对象{flush:'post'}
的语法糖
推迟watchEffect触发时机!组件更新前触发!也就是在生命周期onBeforeUpdate
和 onUpdated
之间触发
语法:
//推迟触发watchEffect
watchEffect(
() => {
/* ... */
},
{
flush: 'post'
}
)
//Vue3.2语法糖watchPostEffect
watchPostEffect(()=>{
/* ... */
})
实例:
//实验watchEffect第二参数 flush: 'post'属性
watchEffect(() => {
console.log("实验watchEffect第二参数 {flush: 'post'}属性");
console.log(obj.age);
},{
flush:'post'
})
watchEffect(() => {
console.log("watchEffect正常时机触发");
console.log(obj.age);
})
//生命周期onUpdated
onUpdated(()=>{
console.log('onUpdated()');
})
//生命周期onBeforeUpdate
onBeforeUpdate(()=>{
console.log('onBeforeUpdate()');
})
修改obj.age
时,执行结果:
watchEffect正常时机触发
onBeforeUpdate()
实验watchEffect第二参数 {flush: 'post'}属性
onUpdated()
同步触发watchSyncEffect
watchSyncEffect
是watchEffect可选参数对象{flush:'sync'}
的语法糖
强制效果始终同步触发!效率低!也就是默认watchEffect之前触发
语法:
watchEffect(
() => {
/* ... */
},
{
flush: 'sync'
}
)
//Vue3.2语法糖watchSyncEffect
watchSyncEffect(()=>{
/* ... */
})
watchEffect不能监听对象
//假设修改了对象的属性值-修改了obj.age
const obj = reactive({ name: '小明', age: 18 })
//watchEffect不能监听对象变化
watchEffect(() => {
console.log('watchEffect监听对象变化');
console.log(obj);
})
//watchEffect可以监听对象属性变化
watchEffect(() => {
console.log('watchEffect监听对象属性变化');
console.log(obj.age);
})
//watch监听对象变化
watch(obj, (obj) => {
console.log('watch监听对象变化');
console.log(obj);
})
总结:watchEffect
用来监听能监听基本数据类型,不能监听对象,但能监听对象的属性;watch能监听基本数据类型和对象!
watch
语法:
参数1-被监听数据(形式:单个数据、数组、带返回值的回调函数)
参数2-触发监听的回调函数,无返回值
可选参数3-对象
{immediate: true,deep:true}
,对象含2个可选参数和Vue2参数效果一致
Vue3的watch和Vue2的watch是基本一样的
1、需要指定监听数据
2、惰性,只在被监听数据变化时才触发(immediate属性可以设置在初始化的时候触发)
监听单个数据
参数1被监听数据的形式:
1、单个基本数据类型;
2、回调函数:返回值为单个基本数据类型;
// 侦听一个 getter
//被监听数据传入一个带返回值的回调函数
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 直接侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
监听多个数据(传入数组)
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
官方文档总结
以下代码截取官方文档,从TS代码可以看出很多关于watch和watchEffect函数参数和返回值的细节!
computed
computed
只接收一个getter函数
getter
触发条件:
1、computed返回值首次被读取时
2、getter绑定的响应式变量被修改时
computed
同时接收getter函数对象和setter函数对象
setter
触发条件:computed返回值被修改时
// 只读的
function computed<T>(
getter: () => T,
debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>
// 可写的
function computed<T>(
options: {
get: () => T
set: (value: T) => void
},
debuggerOptions?: DebuggerOptions
): Ref<T>
interface DebuggerOptions {
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
interface DebuggerEvent {
effect: ReactiveEffect
target: any
type: OperationTypes
key: string | symbol | undefined
}
watchEffect
参数1-触发监听回调函数,回调函数可传入一个onInvalidate函数作为参数!
可选参数2-对象,包含3个可选属性flush、onTrack、onTrigger
function watchEffect(
effect: (onInvalidate: InvalidateCbRegistrator) => void,
options?: WatchEffectOptions
): StopHandle
interface WatchEffectOptions {
flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
interface DebuggerEvent {
effect: ReactiveEffect
target: any
type: OperationTypes
key: string | symbol | undefined
}
type InvalidateCbRegistrator = (invalidate: () => void) => void
type StopHandle = () => void
watch
参数1-被监听数据(形式:单个数据、数组、带返回值的回调函数)
参数2-触发监听的回调函数,无返回值
参数3-传入
{immediate: true,deep:true}
对象和Vue2参数效果一致
// 侦听单一源
function watch<T>(
source: WatcherSource<T>,
callback: (
value: T,
oldValue: T,
onInvalidate: InvalidateCbRegistrator
) => void,
options?: WatchOptions
): StopHandle
// 侦听多个源
function watch<T extends WatcherSource<unknown>[]>(
sources: T
callback: (
values: MapSources<T>,
oldValues: MapSources<T>,
onInvalidate: InvalidateCbRegistrator
) => void,
options? : WatchOptions
): StopHandle
type WatcherSource<T> = Ref<T> | (() => T)
type MapSources<T> = {
[K in keyof T]: T[K] extends WatcherSource<infer V> ? V : never
}
// 参见 `watchEffect` 共享选项的类型声明
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // 默认:false
deep?: boolean
}
来源:https://juejin.cn/post/7091477896782413860