1 计算属性 - computed
计算属性与方法的区别是,计算属性有缓存,方法无缓存。即每次调用方法都会重新执行一次,而计算属性会缓存结果。注:计算属性是只读的,可以根据依赖的响应式数据改变,而无法给计算属性重新赋值:
<template>
<div class="person">
姓:<input type="text" v-model="firstName"> <br>
名:<input type="text" v-model="lastName"> <br>
全名:<span>{
{fullName}}</span> <br>
</div>
</template>
<script setup lang="ts">
import {computed, ref} from "vue";
// 数据
let firstName = ref("zhang")
let lastName = ref("san")
// 计算属性(只读)
let fullName = computed(()=>{
return firstName.value + lastName.value
})
// 方法
</script>
理论上,computed为只读属性,但是也有其他办法,可以间接修改。其实就是将计算属性的get和set方法暴露,在set方法中添加修改逻辑,也只是去修改计算属性依赖的响应式数据,从而引起的计算属性的变化,changeFullName方法中的赋值也只是为了去调用set方法,从头到尾也没有直接修改计算属性fullName:
<template>
<div class="person">
姓:<input type="text" v-model="firstName"> <br>
名:<input type="text" v-model="lastName"> <br>
全名:<span>{
{fullName}}</span> <br>
<button @click="changeFullName">修改全名</button>
</div>
</template>
<script setup lang="ts">
import {computed, ref} from "vue";
// 数据
let firstName = ref("zhang")
let lastName = ref("san")
// 计算属性(只读)
// let fullName = computed(()=>{
// return firstName.value + lastName.value
// })
// 计算属性(可读可写)
let fullName = computed({
get(){
return firstName.value + lastName.value
},
set(val){
firstName.value = "li"
lastName.value = "si"
}
})
// 方法
function changeFullName(){
// 从此只是为了去调用fullName的set函数,真正的修改逻辑都在set中
fullName.value = "lisi"
}
</script>
2 监视watch
vue3的watch只能监视以下四种数据:
- ref定义的数据
- reactive定义的数据
- 函数返回一个值(getter函数)
- 一个包含上述内容的数组
(1)监视ref定义的基本类型数据
注:监视的是ref定义的数据,不是ref的value
<template>
<div>
<p>{
{num}}</p>
<button @click="addNum">增加</button>
</div>
</template>
<script setup lang="ts">
import {ref, watch} from 'vue';
// 属性
let num = ref(0)
// 方法
function addNum(){
num.value += 1
}
// 监视
watch(num, (newValue, oldValue)=>{
console.log(newValue, oldValue)
})
</script>
加上停止监视的逻辑:
// 监视
const stopWatch = watch(num, (newValue, oldValue)=>{
console.log(newValue, oldValue)
if(newValue >= 10){
stopWatch()
}
})
(2)监视ref定义的对象类型数据
下面这样的写法只是监视了对象整体,当对象某个数值,比如name、age发生变化时,并不会调用watch。只有当整体发生变化使,比如person.value = {name: "李四", age: 20},才会调用watch:
// 属性
let person = ref({name: "张三", age: 18})
// 监视
watch(person, (newValue, oldValue)=>{
console.log(newValue, oldValue)
})
若想要监视对象内部属性的变化,需要手动开启深度监视。但是如果修改对象的某个值,newValue和oldValue相同,因为它们是同一个对象。只有修改整个对象才会产生新旧数据:
// 属性
let person = ref({name: "张三", age: 18})
// 监视
watch(person, (newValue, oldValue)=>{
console.log(newValue, oldValue)
},{deep: true})
若想要在初始化时立即执行一次watch,则需要另一个参数:
// 监视
watch(person, (newValue, oldValue)=>{
console.log(newValue, oldValue)
},{deep: true, immediate: true})
(3)监视reactive定义的对象类型数据
reactive修改整个对象的方法:
// 方法
function changePerson(){
Object.assign(person, {name: "李四", age: 20})
}
// 监视
watch(person, (newValue, oldValue)=>{
console.log(newValue, oldValue)
})
watch监视reactive定义的对象类型数据,是默认开启深度监听的,且无法关闭。
(4)监视ref或reactive定义的对象类型数据中的某个属性
// 属性
let person = reactive({
name: "张三",
age: 18,
car: {c1: "宝马", c2: "奥迪"}
})
如果我就想监视person中的name基本类型属性,修改其他属性时,不执行监视name的watch:
// 监视
// 正确写法1:
watch(()=>{return person.name}, (newValue, oldValue)=>{
console.log("名字发生了变化", newValue, oldValue)
})
// 正确写法2:
watch(()=> person.name, (newValue, oldValue)=>{
console.log("名字发生了变化", newValue, oldValue)
})
// 错误写法1:这样监视的是整体
watch(person, (newValue, oldValue)=>{
console.log("名字发生了变化", newValue, oldValue)
})
// 错误写法2:报错
watch(person.person, (newValue, oldValue)=>{
console.log("名字发生了变化", newValue, oldValue)
})
如果我就想监视person中的car对象类型属性,监视整体以及某个属性的变化:
//正确写法:可以监视整体发生变化,可以监视某个属性
watch(()=> person.car, (newValue, oldValue)=>{
console.log("车发生了变化", newValue, oldValue)
},{deep: true})
//错误写法:只能监视整体变化,不会监视某个属性变化
watch(()=> person.car, (newValue, oldValue)=>{
console.log("车发生了变化", newValue, oldValue)
})
//错误写法2:只能监视某个属性的变化,不会监视整体发生变化
watch(person.car, (newValue, oldValue)=>{
console.log("车发生了变化", newValue, oldValue)
})
(5)监视上述多个数据
// 监视
watch([()=> person.name, ()=> person.car], (newValue, oldValue)=>{
console.log(newValue, oldValue)
}, {deep: true})
3 watchEffect
watch和watchEffect的区别:watch需要明确指出要监视哪几个数据,watchEffect不需要。当初始化时,watchEffect就会执行一次。现在模拟一个场景:判断某个人的年龄是否成年。
watch写法:
// 监视
watch([()=>person.name, ()=>person.age], (value)=>{
let [name, age] = value
if(age >= 18){
console.log(name,"成年了!")
}
})
watchEffect写法:
watchEffect(()=>{
if(person.age >= 18){
console.log(person.name, "成年了!")
}
})
4 自定义接口和类型
自定义接口文件:
// 定义一个接口,用于限制person对象的具体属性
export interface PersonInter{
id:string,
name:string,
age:number
}
// 定义一个自定义类型
export type Persons = Array<PersonInter>
引用并使用:
<template>
</template>
<script setup lang="ts">
import {type PersonInter,type Persons} from "@/types";
let person:PersonInter = {id:"qwer1234", name:"张三", age:45}
let personList:Persons = [
{id:"qwer1234", name:"张三", age:45},
{id:"qwer1234", name:"张三", age:45},
{id:"qwer1234", name:"张三", age:45}
]
</script>
变成响应式对象数据,两种写法:
//写法1
let personList:Persons = reactive([
{id:"qwer1234", name:"张三", age:45},
{id:"qwer1234", name:"张三", age:45},
{id:"qwer1234", name:"张三", age:45}
])
//写法2
let personList = reactive<Persons>([
{id:"qwer1234", name:"张三", age:45},
{id:"qwer1234", name:"张三", age:45},
{id:"qwer1234", name:"张三", age:45}
])