Vue3中的计算属性和监视属性【5】
计算属性和监视属性
- 1.计算属性:
- 1.概述:
- 2.案例:
- 2.监视属性:
- 1.概述:
- 2.实现:
- 监视通过ref函数创建的基本类型数据:
- 监视通过ref函数创建的对象类型数据:
- 监视通过reative函数创建的对象类型数据(默认开启深度监视):
- 监视通过ref或reative函数创建的对象类型数据中的某个属性;
- 监视存在上述情况数据的数组(监视多个数据):
- 3.watchEffect函数:
- 需求:
- 实现:
- 方式1:通过监视属性实现
- 方式2:通过watcheffect函数实现
- 概述:
- 实现:
1.计算属性:
1.概述:
在vue3中也有和vue2相同的计算属性,即可以通过已有的属性数据计算出没有的计算属性
2.案例:
<template>姓:<input type="text" v-model="firstName"/><br/>名:<input type="text" v-model="lastName"/><br/>全名:<input type="text" v-model="fullName"/></template><script lang="ts" setup name="Perosn">import {computed, ref} from "vue";let firstName = ref('zhang')
let lastName = ref('san')//计算属性 ---- 读/写
let fullName = computed({get() {console.log("执行了计算属性 getter")return firstName.value + "-" + lastName.value;},set(value) {console.log("执行了计算属性 setter", value);let arr = value.split("-");firstName.value = arr[0];lastName.value = arr[1];}
})</script><style scoped></style>
上述案例中,首先通过ref函数创建原始数据firstname和lastname,然后通过计算属性计算出fullname属性。并在回调函数中添加get/set函数,提供了计算属性值的读写操作。注意set函数中必须引起参与计算的属性的变化,否则无法同步更新参与计算的属性的值。因为同vue2中一样,get函数存在缓存机制,只有当数据改变时才会再次执行;
如果对于计算属性的值只读,则可以对计算属性的回调函数进行简写:
//计算属性 --- 只读
/*let fullName = computed(() => {console.log("计算属性执行了")//firstName 》》 ref() >>>RefImpl >>>valuereturn firstName.value + lastName.value;
})*/
测试结果展示:
情况1:修改原始属性数据:get函数重新执行
情况2:修改计算属性的值:set函数执行,同时引发get函数执行
情况3:set函数执行,未引发get函数执行(set函数中未引发参与计算的属性的改变)
2.监视属性:
1.概述:
在vue3中的监视属性可以监视以下几种类型的数据:
-
监视通过ref函数创建的基本类型数据;
-
监视通过ref函数创建的对象类型数据;
-
监视通过reative函数创建的对象类型数据(默认开启深度监视);
-
监视通过ref或reative函数创建的对象类型数据中的某个属性;
-
监视存在上述情况数据的数组(监视多个数据);
2.实现:
监视通过ref函数创建的基本类型数据:
<template><h1>情况一:监视【ref】定义的【基本类型】数据</h1><h2>当前求和为:{{ sum }}</h2><button @click="changeSum">点击sum++</button>
</template><script lang="ts" setup name="Sum">
import {ref, watch} from "vue";//数据
let sum = ref(0);
//方法
const changeSum = () => {sum.value += 1;
}// 情况一:监视【ref】定义的【基本类型】数据
// 函数(可以停止监视) = watch(要监视的变量(ref修饰),方法(新的数据,旧的数据))
let stopWatch = watch(sum, (newValue, oldValue) => {console.log(newValue, oldValue)if (newValue >= 5) {//停止监视stopWatch();}
})</script><style scoped></style>
上述案例中,我们通过ref函数创建了一个基本类型数据sum,并通过单击鼠标事件改变sum的值。然后开启监视属性,监视该数据,但该属性发生变化时,执行对应的回调函数。但与vue2不同的是在vue3中的监视属性会返回一个stopwatch的函数,回调此函数会停止监视,所以可以在逻辑中对监视的数据的值进行判断,是否停止监视,如上面案例中所示:
测试结果展示:
改变sum数据的值:
当sum的值达到停止监视的条件:
可以看出当sum>=5时,虽然sum的值依旧在发生改变,但不再进行监视了;
监视通过ref函数创建的对象类型数据:
注意:如果需要监视此对象的属性变化,则需要开启深度监视
<template><h1>监视【ref】定义的【对象类型】数据,监视的是对象的内存地址</h1><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button><button @click="changePerson">修改整个用户</button></template><script lang="ts" setup name="Person">
import {ref, watch} from "vue";let person = ref({name: '张三',age: 20
})
const changeName = () => {
//修改姓名person.value.name += "~";
}
const changeAge = () => {//修改年龄person.value.age++;
}
const changePerson = () => {person.value = {name: '李四', age: 60};
}//监视【ref】定义的【对象类型】数据,监视的是对象的内存地址,如果要监视对象内部的属性的改变,需要开启深度监视
//三个参数 1.被监视的数据 2.监视的回调函数 3.配置对象(deep:true 开启深度监视)
watch(person, (newValue) => {console.log("person改变了", newValue)
}, {deep: true})</script><style scoped></style>
上述案例中,我们通过ref创建了原始对象类型数据Person,此对象中有name和age两个属性。之后创建了三个单击鼠标事件分别对此对象的两个属性及整个对象进行修改,然后开始监视此对象的变化。注意如果要监视此对象的name属性和age属性的变化,就必须开启深度监视,否则无法监视到属性的变化;
测试结果展示:
测试1:修改对象的属性
测试2:修改整个对象:
测试3:未开启深度监视时修改对象的属性
可以看到在未开启深度监视时,修改对象的属性值并不会引发监视;
监视通过reative函数创建的对象类型数据(默认开启深度监视):
<template><h1>监视reactive定义的【对象类型】数据,并默认开启深度监视</h1><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button><button @click="changePerson">修改整个用户</button><button @click="obj.a.b.c.d++">{{ obj.a.b.c.d }}++</button></template><script lang="ts" setup name="Persons">
import {reactive, ref, watch} from "vue";
//通过reative函数创建原始对象类型数据
let person = reactive({name: '张三',age: 20
})
//通过reative函数创建原始嵌套对象类型数据
let obj = reactive({a: {b: {c: {d: 555}}}
})const changeName = () => {
//修改姓名person.name += "~";
}
const changeAge = () => {//修改年龄person.age++;
}
const changePerson = () => {//reactive >> proxy >> person ={name: '张三', age: 20}// person = {name: '李四', age: 60};Object.assign(person, {name: '李四', age: 60})
}
//监视 reactive 定义的【对象类型】数据,并默认开启深度监视
watch(person, (newValue, oldValue) => {console.log("person改变了", newValue, oldValue)
})
watch(obj, (newValue, oldValue) => {console.log("obj改变了", newValue, oldValue)
})
</script><style scoped></style>
上述案例中,我们通过reative函数创建了一个原始对象类型数据person和一个嵌套对象类型数据obj,然后通过单击鼠标事件修改原始对象数据的属性值,或修改整个原始对象类型数据。在通过监视属性对上述原始对象类型数据的变化进行监视。注意此种方式默认开启深度监视,所以我们不再需要手动开启;
测试结果展示:
测试1:修改对象类型数据的属性
测试2:修改整个对象
测试3:修改嵌套对象深层处的属性
监视通过ref或reative函数创建的对象类型数据中的某个属性;
<template><h1>监视ref或者reactive定义的【对象类型】数据中的某一个属性</h1><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><h2>汽车:{{ person.car.c1 }}</h2><h2>汽车:{{ person.car.c2 }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button><button @click="changeC1">修改第一台车</button><button @click="changeC2">修改第二台车</button><button @click="changeCar">修改整台车</button></template><script lang="ts" setup name="Person">
import {reactive, ref, watch} from "vue";let person = reactive({name: '张三',age: 20,car: {c1: '雅迪',c2: '9号'}
})const changeName = () => {
//修改姓名person.name += "~";
}
const changeAge = () => {//修改年龄person.age++;
}
const changeC1 = () => {person.car.c1 = "小牛";
}
const changeC2 = () => {person.car.c2 = "五菱";
}
const changeCar = () => {person.car = {c1: '奥迪', c2: '奔驰'};
}
// 监视ref或者reactive定义的【对象类型】数据中的某一个属性
//该属性为基本类型数据 使用函数式写法
watch(() => person.name, (newValue, oldValue) => {console.log("person.name改变了", newValue, oldValue)
})
//该属性为对象类型,可以直接写,或者写为函数式 推荐函数写法
watch( () => person.car, (newValue, oldValue) => {console.log("person.car改变了", newValue, oldValue)
},{deep:true})</script><style scoped></style>
上述案例中,首先通过reative函数创建了一个原始对象类型数据person,在此对象中有基本类型属性name和age,还有一个对象类型属性Car,在对象类型属性Car中又有两个基本类型属性c1和c2.之后定义了一些单击鼠标事件,用于修改这些属性的值,之后在开启监视属性,监视这些属性的变化;
注意:
** 监视的属性为基本类型时,在监视属性中的第一个参数必须写成函数式,如果监视的属性为对象类型,可以直接写,也可以写为函数式,但推荐写为函数式;**
** 如果需要监视对象类型属性内部的属性变化,需要手动开启深度监视,因为虽然reative创建对象属性会默认开启深度监视,但是这个深度监视只是原始对象数据person层面的,如要开启此对象内部对象类型属性的内部属性的监视,则需要手动开启;**
测试结果展示:
测试1:修改基本类型属性的值
测试2:修改对象类型属性的某个属性:
测试3:修改整个对象
监视存在上述情况数据的数组(监视多个数据):
当监视的数据有多个时,在监视属性中的第一个参数(表示被监视的数据)用数组表示;
<template><h1>监视上述的多个数据</h1><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><h2>汽车:{{ person.car.c1 }}</h2><h2>汽车:{{ person.car.c2 }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button><button @click="changeC1">修改第一台车</button><button @click="changeC2">修改第二台车</button><button @click="changeCar">修改整台车</button></template><script lang="ts" setup name="Person">
import {reactive, ref, watch} from "vue";let person = reactive({name: '张三',age: 20,car: {c1: '雅迪',c2: '9号'}
})const changeName = () => {
//修改姓名person.name += "~";
}
const changeAge = () => {//修改年龄person.age++;
}
const changeC1 = () => {person.car.c1 = "小牛";
}
const changeC2 = () => {person.car.c2 = "五菱";
}
const changeCar = () => {person.car = {c1: '奥迪', c2: '奔驰'};
}watch([() => person.name, () => person.car], (newValue, oldValue) => {console.log("name,car改变了", newValue, oldValue)
},{deep:true})</script><style scoped></style>
此案例与上面案例4类似,不同点在于通过一个监视属性同时监视了多个数据,包括原始对象类型数据的基本类型属性,对象类型属性及对象类型属性内部的属性的变化;
测试结果展示:
测试1:修改对象数据的基本类型属性
测试2:修改对象数据的对象属性中的属性
测试3:修改整个对象类型属性
3.watchEffect函数:
需求:
现有这样一个需求:现需要对水温和水位进行监视,当水位>=20或水温>=50时,向服务器发起请求,要如何实现上述需求:
实现:
方式1:通过监视属性实现
按上面的监视属性来看可以这样实现:
<template><h1>需求:水温与水位监测,当水温到50°C或者水位到20CM时,联系服务器</h1><h2>水温:{{ temp }}°C</h2><h2>水位:{{ height }}CM</h2><button @click="changeTemp">水温++</button><button @click="changeHeight">水位++</button>
</template><script lang="ts" setup name="WatchTemp">
import {ref, watch, watchEffect} from "vue";let temp = ref(0);
let height = ref(0);const changeTemp = () => {temp.value += 5;
}
const changeHeight = () => {height.value += 10;
}watch([temp, height], (value) => {//从value中获取 temp 与 height值// console.log(value)let [newTemp, newHeight] = value;if (newTemp >= 50 || newHeight >= 20) {console.log("联系服务器")}
})
</script><style scoped></style>
在上面案例中我们通过ref创建了两个基本类型数据水温和水位,然后通过鼠标单击事件改变这两个数据的值,之后开启监视属性,通过监测的值(数组)来获取到对应的原始数据的值,再判断当这两个数值达到限定条件时,联系服务器;
测试结果展示:
测试1:水温达到50及以上
测试2:水位达到20及以上
方式2:通过watcheffect函数实现
概述:
虽然通过上述方式可以实现,但过程相对比较繁琐;实际上在vue3中提供了一个effect函数,此函数会在页面初次渲染时执行一次,之后当此函数中的依赖属性发生变化时再次执行,这样相对上面的方式来说要更简洁,因为不用从监视的值(数组)中获取到参与监视的属性的值,而可以直接写;
实现:
<template><h1>需求:水温与水位监测,当水温到50°C或者水位到20CM时,联系服务器</h1><h2>水温:{{ temp }}°C</h2><h2>水位:{{ height }}CM</h2><button @click="changeTemp">水温++</button><button @click="changeHeight">水位++</button>
</template><script lang="ts" setup name="WatchTemp">
import {ref, watch, watchEffect} from "vue";let temp = ref(0);
let height = ref(0);const changeTemp = () => {temp.value += 5;
}
const changeHeight = () => {height.value += 10;
}watchEffect(() => {//当温度与水位到预设值后if (temp.value >= 50 || height.value >= 20) {console.log("联系服务器")}
})
</script><style scoped></style>
此方式相较方式1就更加简洁化,在此函数中会自动监视所依赖的数据,所以直接写水温和水位的值可以了,不必再通过更新后的value(数组)中获取到水温和水位的值了;
测试结果展示:
测试结果展示:
测试1:水温达到50及以上
测试2:水位达到20及以上