详解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的第二参数:带有 onTrackonTrigger 选项的对象

  • 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

watchPostEffectwatchSyncEffect在Vue3.2新增,是watchEffect类似语法糖的东西,

watchEffect可选参数对象{ flush?: 'pre' | 'post' | 'sync'}中post和sync的语法糖,pre是默认值

推迟触发watchPostEffect

watchPostEffect 是watchEffect可选参数对象{flush:'post'}的语法糖

推迟watchEffect触发时机!组件更新前触发!也就是在生命周期onBeforeUpdateonUpdated之间触发

语法:

//推迟触发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

标签:Vue,Computed,watch
0
投稿

猜你喜欢

  • Pygame框架实现飞机大战

    2023-09-15 02:14:24
  • 整理各种js按比例缩放图片方法

    2007-09-27 20:01:00
  • 如何使用flask将模型部署为服务

    2021-11-11 06:02:48
  • Centos6.5在线安装mysql 8.0详细教程

    2024-01-15 01:02:57
  • 新版php study根目录下文件夹无法显示的图文解决方法

    2023-11-15 00:44:29
  • 网页栅格系统研究(3):粒度问题

    2008-10-28 19:46:00
  • Python中的Numpy矩阵操作

    2021-10-06 07:19:23
  • WEB前端开发高性能优化之JavaScript优化细节

    2009-06-10 14:38:00
  • python 实时得到cpu和内存的使用情况方法

    2023-10-05 20:03:24
  • python爬虫---requests库的用法详解

    2022-11-19 10:08:19
  • Python中使用logging模块打印log日志详解

    2021-10-01 02:32:17
  • 用Python制作在地图上模拟瘟疫扩散的Gif图

    2022-09-26 17:20:22
  • es6函数之严格模式用法实例分析

    2023-08-09 06:15:40
  • 什么是Dynamic HTML(Dhtml)

    2010-03-16 12:40:00
  • python 使用pygame工具包实现贪吃蛇游戏(多彩版)

    2021-05-10 17:29:19
  • python开发一个解析protobuf文件的简单编译器

    2021-08-11 12:01:22
  • Python 实现删除某路径下文件及文件夹的实例讲解

    2021-05-07 08:31:49
  • 如何将sql执行的错误消息记录到本地文件中实现过程

    2024-01-22 06:50:37
  • 添加到各大流行网摘 书签的代码

    2008-04-20 14:15:00
  • 兼容主流浏览器的【表情插入】源码

    2010-07-02 13:02:00
  • asp之家 网络编程 m.aspxhome.com