vue3入门知识(二)
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>