computed
计算属性是有缓存的,方法没有缓存
计算属性得到的数据是一个ref定义的响应式数据
<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" name="Person">
import { ref, computed } from 'vue';let firstName = ref('张');
let lastName = ref('三');// 这样定义的计算属性是只读的
// let fullName = computed(() => {
// return firstName.value + '-' + lastName.value;
// });// 这样定义的计算属性是可读可写的
let fullName = computed({get() {return firstName.value + '-' + lastName.value;},set(newValue) {const names = newValue.split('-');firstName.value = names[0];lastName.value = names[1];}
});function changeFullName() {fullName.value = '李-四';
};
</script>
watch
监视数据的变化,vue3中watch只能监视以下四种数据:
1. ref定义的数据
2. reactive定义的数据
3. 函数返回一个值(getter函数)
4. 一个包含上述内容的数组
1. 监视ref定义的【基本类型】数据:直接写数据名即可,监视的是其value值的改变
<template><div class="person"><h2>当前求和为:{{ sum }}</h2><button @click="changeSum">点我sum+1</button></div>
</template><script setup lang="ts" name="Person">
import { ref, watch } from 'vue';let sum = ref(0);// const stopWatch = watch(sum, (newVal, oldVal) => {
// console.log('sum.value changed: ', newVal, oldVal);
// if (newVal > 10) {
// // 当 sum 大于10时,取消监听
// stopWatch();
// }
// });watch(sum, (newVal, oldVal) => {console.log('sum.value changed: ', newVal, oldVal);
});function changeSum() {sum.value++;
};
</script>
2. 监视ref定义的【对象类型】数据:直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视
<template><div class="person"><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><button @click="changeName">修改姓名</button><button @click="changeAge">修改年龄</button><button @click="changePerson">修改</button></div>
</template><script setup lang="ts" name="Person">
import { ref, watch } from 'vue';let person = ref({name: '张三',age: 18,
});// watch的第一个参数是:被监视的数据
// watch的第二个参数是:监视的回调函数
// watch的第三个参数是:配置对象(deep、immediate等)// 监视的是对象的地址值
// watch(person, (newVal, oldVal) => {
// console.log('person changed', newVal, oldVal);
// });// 监视对象内部属性的变化 手动开启深度监听 立即执行 immediate: true
watch(person, (newVal, oldVal) => {// 当对象整体被替换时 newVal 和 oldVal 不同// 当对象中某个属性被修改 newVal 和 oldVal 相同console.log('person changed', newVal, oldVal);
}, { deep: true });function changeName() {person.value.name += '~';
};
function changeAge() {person.value.age++;
};
function changePerson() {person.value = {name: '李四',age: 80,};
}
</script><style scoped lang="less">
.person {button {margin: 0 10px;}
}
</style>
3. 监视reactive定义的【对象类型】数据,且默认开启了深度监视
<template><div class="person"><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><button @click="changeName">修改姓名</button><button @click="changeAge">修改年龄</button><button @click="changePerson">修改</button></div>
</template><script setup lang="ts" name="Person">
import { reactive, watch } from 'vue';let person = reactive({name: '张三',age: 18,
});// 监视【reactive】定义的【对象类型】数据 默认是开启深度监听的 且无法关闭
watch(person, (newVal, oldVal) => {// newVal 和 oldVal 相同 因为修改时操作的是同一个对象console.log('person changed', newVal, oldVal);
});function changeName() {person.name += '~';
};
function changeAge() {person.age++;
};
function changePerson() {Object.assign(person, {name: '李四',age: 80,});
}
</script><style scoped lang="less">
.person {button {margin: 0 10px;}
}
</style>
4. 监视ref或reactive定义的【对象类型】数据中的某个属性
(1)若该属性不是【对象类型】,需要写成函数形式
(2)若该属性依然是【对象类型】,可直接写,也可写成函数,建议写成函数
<template><div class="person"><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2><button @click="changeName">修改姓名</button><button @click="changeAge">修改年龄</button><button @click="changeC1">修改第一台车</button><button @click="changeC2">修改第二台车</button><button @click="changeCar">修改整个车</button></div>
</template><script setup lang="ts" name="Person">
import { reactive, watch } from 'vue';let person = reactive({name: '张三',age: 18,car: {c1: '奔驰',c2: '宝马',},
});// 监视响应式对象中的某个属性 且该属性是基本类型的 要写成函数式
watch(() => { return person.name }, (newVal, oldVal) => {// newVal 和 oldVal 不同console.log('person.name changed', newVal, oldVal);
});// 监视响应式对象中的某个属性 且该属性是对象类型的 建议写成函数式否则整体修改时监听不到 需要加深度监听
watch(() => { return person.car }, (newVal, oldVal) => {// newVal 和 oldVal 不同console.log('person.car changed', newVal, oldVal);
}, { deep: true });function changeName() {person.name += '~';
};
function changeAge() {person.age++;
};
function changeC1() {person.car.c1 = '奥迪';
};
function changeC2() {person.car.c2 = '保时捷';
};
function changeCar() {person.car = {c1: '11',c2: '22',};
};
</script><style scoped lang="less">
.person {button {margin: 0 10px;}
}
</style>
5. 监视响应式对象中的某几个属性
<template><div class="person"><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2><button @click="changeName">修改姓名</button><button @click="changeAge">修改年龄</button><button @click="changeC1">修改第一台车</button><button @click="changeC2">修改第二台车</button><button @click="changeCar">修改整个车</button></div>
</template><script setup lang="ts" name="Person">
import { reactive, watch } from 'vue';let person = reactive({name: '张三',age: 18,car: {c1: '奔驰',c2: '宝马',},
});// 监视响应式对象中的某几个属性
watch([() => { return person.name }, () => { return person.car.c1 }], (newVal, oldVal) => {console.log('person.car changed', newVal, oldVal);
}, { deep: true });function changeName() {person.name += '~';
};
function changeAge() {person.age++;
};
function changeC1() {person.car.c1 = '奥迪';
};
function changeC2() {person.car.c2 = '保时捷';
};
function changeCar() {person.car = {c1: '11',c2: '22',};
};
</script><style scoped lang="less">
.person {button {margin: 0 10px;}
}
</style>
watchEffect
立即执行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数
watch 和 watchEffect 对比:
1. 都能监听响应式数据的变化,监听数据变化的方式不同
2. watch:要明确指出监视的数据
3. watchEffect:不用明确指出监视的数据(其中用到哪些数据就监视哪些属性)
<template><div class="person"><h2>当前水温:{{ temp }}度</h2><h2>当前水位:{{ height }}cm</h2><button @click="changeTemp">水温+10</button><button @click="changeHeight">水位+10</button></div>
</template><script setup lang="ts" name="Person">
import { ref, watch, watchEffect } from 'vue';const temp = ref(10);
const height = ref(0);// watch([temp, height], (newVal) => {
// const [t, h] = newVal;
// if(t >= 60 || h >= 80) {
// console.log('发送请求');
// }
// });watchEffect(() => {// 会立即执行一次console.log('执行了');if(temp.value >= 60 || height.value >= 80) {console.log('发送请求');}
});function changeTemp() {temp.value += 10;
};
function changeHeight() {height.value += 10;
};
</script><style scoped lang="less">
.person {button {margin: 0 10px;}
}
</style>
标签的ref
1. 用在普通DOM标签上,获取的是DOM节点
2. 用在组件标签上,获取的是组件实例对象
// 父组件
<template><h2>中国</h2><h2 ref="title2">北京</h2><h2>朝阳</h2><button @click="showLog">输出</button><Person ref="ren"></Person>
</template><script lang="ts" setup name="App">
import Person from './components/Person.vue';
import { ref } from 'vue';const title2 = ref();
const ren = ref();function showLog() {// console.log(title2.value); // 输出html标签console.log(ren.value); // 输出组件实例
}
</script>
// 子组件
<template><div class="person"></div>
</template><script setup lang="ts" name="Person">
import { ref, defineExpose } from 'vue';const a = ref(0);
const b = ref(1);
const c = ref(2);// 在这里暴露出去的值 父组件才可以拿到
defineExpose({a, b});
</script>
接口、自定义类型、泛型
// src/types/index.ts
// 定义一个接口 用于限制person对象的具体属性
export interface PersonInter {name: string,age: number,id: string,
};// 一个自定义类型
// export type Persons = Array<PersonInter>; // 泛型
export type Persons = PersonInter[];
<template><div class="person"></div>
</template><script setup lang="ts" name="Person">
import { type PersonInter, type Persons } from '@/types';const person:PersonInter = {name: '张三',age: 18,id: '1',
};const persons:Array<PersonInter> = [{name: '张三',age: 18,id: '1',},{name: '李四',age: 18,id: '2',},
];const personList:Persons = [{name: '张三',age: 18,id: '1',},{name: '李四',age: 18,id: '2',},
];
</script>
组件通信--父传子
<template><div class="person"><!-- <h2>{{ a }}</h2> --><!-- <h2>{{ list }}</h2> --><ul><li v-for="item in list" :key="item.id">{{ item.name }} -- {{ item.age }}</li></ul></div>
</template><script setup lang="ts" name="Person">
// defineProps不用引入也可以使用
// 在vue3中 defineXXX 叫作宏函数 不用引入也可以使用
import { defineProps, withDefaults } from 'vue';
import {type Persons} from '@/types'// 接收a 同时保存起来 可以在脚本中使用
// const x = defineProps(['a', 'b']);
// console.log(x.a);// 接收list 但这样无法在脚本中使用
// defineProps(['list']);// 接收list 并限制类型
// defineProps<{list:Persons}>();// 接收list 并限制类型 并限制必要性 并指定默认值
withDefaults(defineProps<{list?:Persons}>(), {list: () => [{name: '张三',age: 18,id: '1',},{name: '李四',age: 20,id: '2',}]
});
</script>