【Vue3】常用API
1.ref() 创建基本类型的响应数据
在Vue2中,写在data()中的数据是响应式的。但是Vue3中数据不定义在data()中,那要如何定义响应式数据呢?
通过ref()包装后的数据将成为响应式数据,可以在html结构中使用mustache语法( {{ }}: 双花括号 )被使用。
// 引入ref
import { ref } from 'vue'
let num = ref(123) // 定义number类型数据
let str = ref('hello world') //定义string类型数据
let bool = ref(true) //定义布尔类型数据
// ref也可以用来创建引用类型的响应式数据,但是并不建议这么做
let obj = ref({
name: 'Nimo',
age: 12
})
// 如果要调用引用类型的值
let name = obj.value.name // 可以这么使用,必须通过对象的value属性获取
// 使用的时候,值其实是在该对象的 value属性中
let num2 = num.value // 通过.value可以调取到值
注意:虽然ref()也可以用来创建引用类型值如:Object,Array...但是更建议使用reactive()来创建引用对象的响应式数据
2.reactive()创建引用类型的响应式数据
使用ref()虽然也能创建引用类型的响应式数据,但是更建议通过reactive()创建。
// 引入reactive()
import { reactive } from 'vue'
//使用reactive创建对象类型的响应式数据
let obj = reactive({
name: 'Nimo',
age: 12
})
// 使用reactive创建数组类型的响应式数据
let arr = reactive([
{id: 1, title: 'this is title one'},
{id: 2, title: 'this is title two'},
{id: 3, title: 'this is title three'},
])
// 使用reactive创建深层次的响应式数据
let deepObj = reactive({
first:{
second: {
third: 'word'
}
}
})
// 调用创建的数据可以直接调用
let val1 = obj.name // 获取存在obj中的name属性
let val2 = arr[0].title // 获取存储在数组arr中第一项的title属性
let val3 = deepObj.first.second.third // 深层的数据也可直接通过.的方式获取
注意:不像ref()既能创建基本类型也能创建引用类型。reactive()只能用于创建引用类型的响应式数据。
使用reactive的好处:reactive()定义的数据可以直接通过 .xxx获取到,不像ref()定义的数据必须通过.value获取
// 使用ref定义引用类型的缺点
// 1. 访问对象的值的时候必须通过.value属性来访问。会影响代码的整洁性。
// 2. 解构丢失响应性
let obj = ref({
name: 'Nimo',
age: 12
})
// 解构obj对象后,name和age将不再具有响应性
let {name, age} = obj
//3. 对于多层次的应用类型数据,使用ref定义只能对最浅层的数据进行响应式监听,对于深层次的数据不会有响应性
// 4. 在Typescript中使用ref()时,对于类型的推断不如预期的那么直接。
3.toRef()和toRefs():把数据转化成响应式
上文中提到,当使用ref()定义了引用类型的响应式数据后,一旦解构这个数据,解构后的变量将不再是响应式的。toRef()可以解决这个问题
import { ref, toRef } from 'vue'
// 使用ref()创建响应式数据
let obj = ref({
name: 'Nimo',
age: 12
})
// 解构obj对象后,name和age将不再具有响应性
let {name, age} = obj
// 使用toRefs()包裹对象可将解构该对象后的数据变为响应式
let {name, age} = toRefs(obj)
// 使用toRef()可将对象中的某一属性变成响应式
let name2 = toRef(obj, 'name') // 将obj对象中的name属性变成响应式
4.computed() 计算属性
在Vue2中也有计算属性的概念,Vue2中使用选项式API的方式使用computed。在Vue3中要使用computed必须要引入computed的API。
import { ref, computed } from 'vue'
// 使用ref()创建数据姓和名
let firstName = ref('Nimo')
let lastName = ref('kiko')
// 使用computed将姓名拼接
// 注意:这么写的计算属性是只读的,但是计算属性可以通过getter和setter属性创建可读可写的计算属性
let fullName = computed( (res) => {
return {
firstName.value + lastName.value
}
})
// 使用计算属性的getter和setter方法
// get()和set()方法必须都写
let fullName = computed({
get() {
return {
firstName.value + lastName.value
}
},
set(value){
// 根据 '-' 将字符串分割开
let [str1, str2] = value.split('-')
firstName.value = str1
lastName.value = str2
}
})
// 修改fullName的函数
function changeFullName() {
fullName.value = 'zhang-san'
}
changeFullName()
5.watch()监听的五种情况
Vue3中使用watch对数据进行监听需要引入watch()API。
import { ref, reactive, watch } from 'vue'
// 情况一:监听【ref】创建的【基本类型】数据
let sum = ref(0)
function() {
sum.value++
}
// watch()方法返回一个函数stopWatch(),调用这个方法可以停止监听
const stopWatch = watch(sum, (newVal, oldVal) => {
console.log(newVal, oldVal)
if( newVal > 10 ) {
stopWatch()
}
})
// 情况二:监听【ref】定义的【对象类型】数据
// 监视的是对象的地址值,若想监视对象内部属性的变化需要手动开启深度监视
// 第三个参数是一个对象,对象内部有个deep属性,值为true时开启深度监视
// 如果修改的是ref中定义的属性,新值和旧值都是新值,因为他们是同一个对象
// 如果修改的是整个ref定义的对象,新值是新值,旧值是旧值,因为他们是不同的对象
let person = ref({
name: "zhangsan",
age: 18,
});
// 方法
function changeName() {
person.value.name = "lisi";
}
watch(person,(newval, oldval) => {
console.log("person变化了", newval, oldval);
},
{ deep: true }
);
// 情况三:监视【reactive】定义的【对象类型】数据
// 如果监听的是reactive创建的数据则默认开启深度监听
// 隐式创建监听,无法关闭
// reactive创建的对象类型数据,newValue和oldValue会相等,因为地址值没有被修改
watch(person,(newval, oldval) => {
console.log("person变化了", newval, oldval);
},
{ deep: true }
);
// 情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性
// 1.如果监视的对象是一个基本类型数据,需要将监视对象包装成一个getter函数,这样就可以监视对象中的某个属性
// 2.如果监视的对象是一个对象类型数据,可以直接监视对象中的某个属性,但如果要监视对象整体是不行的,需要将对象包装成一个getter函数
// 同时需要开启深度监听{deep:true}
// 最佳实践:监视的要是对象里的属性,最好写函数式,注意:若是对象,监视的是地址值;需要关注对象内部,需要手动开启深度监视。
watch(() => person.name,(nval, oval) => {
console.log("person.name变化了", nval, oval);
}
);
// 情况五:监视上述的多个数据
// 传入一个数组,数组的每一项是用函数式包裹的要监听的属性
watch([() => person.name, () => person.age],(nval, oval) => {
console.log("person4.name或person4.car.c1变化了", nval, oval);
},{deep:true}
);
6.watchEffect() 监听数据
watchEffect()也可用来监听响应式数据的变化
import { ref, watchEffect } from "vue";
// 数据
let temp = ref(10);
let height = ref(0);
// 使用watchEffect
// watch和watchEffect的区别:
// 1. 都能监听响应式数据的变化,但监听数据变化的方式不同
// 2. watch要明确指出监听的数据
// 3. 使用watchchEffect时,不需要传递参数(函数中有哪些属性,就监视哪些属性)
watchEffect(() => {
if(temp.value >= 60 || height.value >= 80){
console.log("发送请求");
}
})
7.defineProps() 接受参数
在Vue2中,组件通过props:{}属性接收参数,在Vue3中则需要使用defineProps()接收参数。
// 假设父组件中传给子组件一个参数为list
// 子组件通过defineProps()接收参数
// 注意:defineProps()为宏函数,不需要引入
defineProps(['list'])
// 如果需要给参数设置默认值则需要引入withDefaults()
import { withDefaults } from 'vue'
//定义一个接口
interface PersonInter {
id: string;
name: string;
age: number;
}
// 定义一个类型
type Persons = Array<PersonInter>
// 使用withDefault()为参数设置默认值
// 接受list + 限制类型
withDefaults(defineProps<list:Persons>(), {
list: () => {
return [{ id: "123456", name: "张三", age: 182 }]
}
})
8.生命周期的API
在Vue2中,生命周期的钩子函数有如下:
- 创建:
- beforeCreate()
- created()
- 挂载:
- beforeMount()
- mounted()
- 更新:
- beforeUpdate()
- updated()
- 销毁:
- beforeDestroy()
- destroyed()
在Vue3中生命周期函数需要引入才能使用
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from "vue";
// 注意:在Vue3中,创建阶段的生命周期函数合并入了setup()中
// onBeforeMount: 在组件挂载到页面之前执行
onBeforeMount(() => {
console.log("beforeMount");
});
// onMounted: 在组件挂载到页面之后执行
onMounted(() => {
console.log("mounted");
});
// onBeforeUpdate: 在组件更新之前执行
onBeforeUpdate(() => {
console.log("beforeUpdate");
});
// onUpdated: 在组件更新之后执行
onUpdated(() => {
console.log("updated");
});
// 注意:在Vue3中 销毁阶段的生命周期函数从destroy改为unmounted
// onBeforeUnmount: 在组件卸载之前执行
onBeforeUnmount(() => {
console.log("beforeUnmount");
});
// onUnmounted: 在组件卸载之后执行
onUnmounted(() => {
console.log("unmounted");
});