赞
踩
computed( { get(){ return X }, set(val){ } } )
<template> <p>firstName: <input type="text" v-model="person.firstName" /></p> <p>lastName: <input type="text" v-model="person.lastName" /></p> <p>fullName: <input type="text" v-model="person.fullName" /></p> </template> <script setup lang="ts"> import { reactive, computed } from "vue"; import type { WritableComputedRef } from "vue"; interface Person { firstName: string; lastName: string; fullName?: WritableComputedRef<string>; } let person: Person = reactive({ firstName: "super", lastName: "man" }); person.fullName = computed({ // getter get: () => person.firstName + "-" + person.lastName, // setter set(val) { let temp = val.split("-"); person.firstName = temp[0]; person.lastName = temp[1]; }, }); </script>
computed(callback)
<template> <p>firstName: <input type="text" v-model="person.firstName" /></p> <p>lastName: <input type="text" v-model="person.lastName" /></p> <p>fullName: {{ person.fullName }}</p> </template> <script setup lang="ts"> import { reactive, computed } from "vue"; import type { WritableComputedRef } from "vue"; interface Person { firstName: string; lastName: string; fullName?: WritableComputedRef<string>; } let person: Person = reactive({ firstName: "super", lastName: "man" }); person.fullName = computed(() => person.firstName + "-" + person.lastName); </script>
类型标注
computed()
会自动从其计算函数的返回值上推导出类型person.fullName = computed<string>(() => person.firstName + "-" + person.lastName);
计算属性 vs 方法
计算属性能实现的 函数也能实现。不同的是,计算属性的值会根据其响应性依赖进行缓存。一个计算属性只会在其响应性依赖更新时才重新执行,只要其响应性依赖不更新,获取的就是缓存值。而函数会在每次获取数据时都执行一次。
注意事项
监听 ref 定义的基本类型数据:watch( 变量名, (oldVal, newVal) => {}[, 配置对象] )
<template> <p>name: {{ name }}</p> <button @click="name += '~'">点击修改 name</button> </template> <script setup lang="ts"> import { ref, watch } from 'vue'; const name = ref('superman'); watch( name, (oldVal, newVal) => { console.log('oldVal', oldVal); console.log('newVal', newVal); }, { immediate: true } // 强制侦听器的回调在组件加载完毕后立即执行一次 ); </script>
deep: false
,也还是会深度监听)oldVal
会与 newVal
一样,因为它们是同一个对象(引用类型数据操作的是内存地址)watch( 变量名, (oldVal, newVal) => {}[, 配置对象] )
<template> <p>msg.job.j1.salary: {{ msg.job.j1.salary }}</p> <button @click="msg.job.j1.salary++">点击修改</button> </template> <script setup lang="ts"> import { reactive, watch } from 'vue'; const msg = reactive({ job: { j1: { salary: 20 } } }); watch( msg, (oldVal, newVal) => { console.log('oldVal', oldVal); console.log(oldVal === newVal); // true }, { deep: false } /* 会强制深度监听, deep: false 无效 */ ); </script>
watch( 变量名.value, (oldVal, newVal) => {}[, 配置对象] )
import { ref, watch } from 'vue';
const msg = ref({ job: { j1: { salary: 20 } } });
watch(msg.value /* 需要使用 `.value` 指定 ref 定义的引用类型数据 */, (oldVal, newVal) => {
console.log('oldVal', oldVal);
console.log(oldVal === newVal);
});
watch( () => 变量名.属性名, (oldVal, newVal) => {}[, 配置对象] )
<template> <p>msg.age: {{ msg.age }}</p> <button @click="msg.age++">点击修改</button> </template> <script setup lang="ts"> import { reactive, watch } from 'vue'; let msg = reactive({ age: 21 }); watch( () => msg.age, (oldVal, newVal) => { console.log('oldVal', oldVal); console.log('newVal', newVal); } ); </script>
watch( () => 变量名.value.属性名, (oldVal, newVal) => {}[, 配置对象] )
import { ref, watch } from 'vue';
const msg = ref({ age: 21 });
watch(
() => msg.value.age, // 需要使用 `.value` 指定 ref 定义的引用类型数据
(oldVal, newVal) => {
console.log('oldVal', oldVal);
console.log('newVal', newVal);
}
);
oldVal
等于 newVal
,因为它们是同一个对象(引用类型数据操作的是内存地址)watch( () => 变量名.属性名, (oldVal, newVal) => {}[, 配置对象] )
<template> <p>msg.job.j1.salary: {{ msg.job.j1.salary }}</p> <button @click="msg.job.j1.salary++">点击修改</button> </template> <script setup lang="ts"> import { reactive, watch } from 'vue'; const msg = reactive({ job: { j1: { salary: 20 } } }); watch( () => msg.job, (oldVal, newVal) => { console.log('oldVal', oldVal); // oldVal Proxy {j1: {…}} console.log(oldVal === newVal); // true }, { deep: true } // 设置 deep: true 深度监听 ); </script>
watch( () => 变量名.value.属性名, (oldVal, newVal) => {}[, 配置对象] )
<template> <p>msg.job.j1.salary: {{ msg.job.j1.salary }}</p> <button @click="msg.job.j1.salary++">点击修改</button> </template> <script setup lang="ts"> import { ref, watch } from 'vue'; let msg = ref({ job: { j1: { salary: 20 } } }); watch( () => msg.value.job, // 需要使用 `.value` 指定 ref 定义的引用类型数据 (oldVal, newVal) => { console.log('oldVal', oldVal); console.log(oldVal === newVal); }, { deep: true } ); </script>
<template> <p>faMsg: {{ faMsg }}</p> <button @click="faMsg[0] += 1">修改</button> </template> <script setup lang="ts"> import { reactive, watch } from 'vue'; const faMsg = reactive([1, 2, 3]); watch( () => [...faMsg], // 回调函数 + 解构赋值 (oldVal, newVal) => { console.log('oldVal', oldVal); console.log('newVal', newVal); } /* 没有设置 deep: true */ ); </script>
对于对象数据,我们可以使用 [ lodash 的 cloneDeep ] + [回调函数],以获取 oldVal 和 newVal 值
此时,会强制深度监听
<template> <p>msg.job.j1.salary: {{ msg.job.j1.salary }}</p> <button @click="msg.job.j1.salary++">修改</button> </template> <script setup lang="ts"> import { reactive, watch } from 'vue'; import _ from 'lodash'; // 引入 lodash let msg = reactive({ job: { j1: { salary: 21 } } }); watch( () => _.cloneDeep(msg), // 使用 _.cloneDeep(数据) (oldVal, newVal) => { console.log('oldVal', oldVal); console.log('newVal', newVal); } /* 没有设置 deep: true */ ); </script>
设置第一参数为 [多个数据源组成的数组],以监听多个数据:watch( [数据1, 数据2], (oldVal, newVal) => {}[, 配置对象] )
<template> <p>name: {{ name }}</p> <p>age: {{ age }}</p> <button @click="name += '~'">点击修改 name</button> <button @click="age++">点击修改 age</button> </template> <script setup lang="ts"> import { ref, watch } from 'vue'; const name = ref('superman'); const age = ref(21); watch([name, age], (oldVal, newVal) => { console.log('oldVal', oldVal); // 旧值组成的数组 console.log('newVal', newVal); // 新值组成的数组 }); </script>
<template> <p>obj.name: {{ obj.name }}</p> <p>num: {{ num }}</p> <button @click="obj.name += '~'">点击修改 obj.name</button> <button @click="num++">点击修改 num</button> </template> <script setup lang="ts"> import { reactive, ref, watchEffect } from 'vue'; let obj = reactive({ name: 'superman' }); let num = ref(0); watchEffect(() => { // 回调函数会在创建侦听器时立即执行 // 只要这里面的响应式数据被修改了, 就会再执行一次 let wObj = obj.name; let wNum = num.value; console.log('watchEffect: ', wObj, wNum); }); </script>
注意:watchEffect
仅会在其同步执行期间,才追踪依赖
在使用异步回调时,只有 [在第一个 await
正常工作前访问到的属性] 会被追踪
与 watch 的不同:
更改响应式状态时,它可能会同时触发 [Vue 组件更新] & [侦听器回调]
默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用
这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态
如果想在侦听器回调中能访问被 Vue 更新之后的DOM,需要配置 flush: 'post'
:
watch(source, callback, { flush: 'post' })
watchEffect(callback, { flush: 'post' })
后置刷新的 watchEffect()
有个更方便的别名 watchPostEffect()
:
import { watchPostEffect } from 'vue'
watchPostEffect(() => { /* 在 Vue 更新后执行 */ })