Vue3常用的API
大约 4 分钟
浅层次的响应式
Vue3 的 ref、reactive 获取的proxy都是深层次的响应式。有时,我们不关心子对象的响应式(或用不上响应式),则使用浅层次的响应可以提高效率。
对应于 ref、reactive,浅层次响应式也提供了两个组合式API;
shallowRef:可以给基本数据类型添加响应式。如果是对象,则不会支持响应式(除非换一个全新的对象)。
shallowRef 返回 ObjectRefImpl 对象,只对 .value 添加了响应式,子级属性没有响应式。
ref 和 shallowRef:
- 用在基本数据类型上,没差。
- 用在对象上,ref是有响应式的,底层会创建Proxy代理对象。shallowRef是没有响应式的,底层也不会创建Proxy对象。
import {shallowRef} from 'vue' setup(){ // 有的时候,这个对象中的属性可能永远都不可能修改,如果要改,也是更换对象。显然这个时候可以使用shallowRef,进行优化。 let data = shallowRef({ counter : 1 }) function changeCounter(){ // 直接更换整个对象 data.value = {counter : 10000} } return {data, changeCounter} }shallowReactive:对象的第一层支持响应式,第二层就不再支持了。
深只读与浅只读
对应的组合式API:readOnly,shallowReadOnly。返回新的代理对象,这个代理对象的不支持set,即只读。
// data 是响应式的
// readOnlyData 也是响应式的,但是只读,不能进行修改。
readOnlyData = readOnly(data) // readOnly 深只读,data的子对象也是只读的
shallowReadOnlyData = shallowReadOnly(data) // 浅只读,data的一级属性只读,子对象仍是可读可写的。
return {readOnlyData, shallowReadOnlyData} // 需要在setup中返回只读常用于保护数据的更改。有时其他组件传来的数据不可以更改,就可以用只读包装一下权限,让这个数据在组件内只读。
响应式数据的判断
Vue3 提供了4个组合式API,供程序动态判断数据是否响应式:
- isRef:判断数据是否为 ref() 创建的响应式
- ifReactive:判断数据是否为 reactive()、shallowReactive() 创建的响应式.
- isReadOnly:判断数据是否为 readOnly()、shallowReadOnly() 创建的响应式(是否只读)。
- isProxy:判断数据是否为 reactive()、shallowReactive()、readOnly()、shallowReadOnly() 创建的响应式。(即 isReactive + isReadOnly)
toRef、toRefs
响应式对象的使用优化:
// 创建响应式对象
let data = reactive({
counter1 : 1,
counter2 : 100,
a : {counter3 : 1000}
})
// 直接返回后,使用其中的数据需要不停的 data. 十分繁琐
return {data}
// {{data.counter1}} {{data.counter2}} {{data.a.counter3}}
// 不再是响应式的了,因为都被解析成基本数据类型返回了
return {
data.counter1, // counter1 : 1
data.counter2, // counter2 : 100
data.a.counter3 // counter3 : 1000
}
// {{counter1}} {{counter2}} {{counter3}}
// 使用 ref 二次包装
// 是响应式的,但 data 不同步,因为是新的响应式对象了
return {
counter1 : ref(data.counter1), // ref(1)
counter2 : ref(data.counter2), // ref(100)
counter3 : ref(data.a.counter3) // ref(1000)
}类似于 Vuex 中的 mapXxx 优化映射。Vue3 提供了 toRef、toRefs 组合式API,编写获取响应式数据一级属性的响应式引用。
// 使用 toRef 创建响应式引用,返回 ObjectRefImpl
// toRef(响应式对象, 属性名)
return {
counter1 : toRef(data,"counter1"),
counter2 : toRef(data,"counter2"),
counter3 : toRef(data.a,"counter3") // data.a 也是响应式的
} // 是响应式的,而且和 data 同步,因为是原来响应式对象的引用
// 使用 toRefs 简化创建响应式引用(一级属性)
// toRefs(响应式对象) 返回 {ObjectRefImpl1 : bjectRefImpl1, bjectRefImpl2: bjectRefImpl2 ...}
return {
...toRefs(data)
// 使用 ES6 合并对象,等同于:
// counter1 : toRef(data,"counter1"),
// counter2 : toRef(data,"counter2"),
// a : toRef(data,"a")
}
// {{counter1}} {{counter2}} {{a.counter3}} 都是响应式的,而且和原响应式对象同步转换为原始、标记为原始
toRaw:获取响应式对象的原始对象。只适用于reactive生成的响应式对象。
data = reactive(...)
rawData = toRaw(data) // 获取原始对象
rawData.xxx = YYY // 通过原始对象进行修改,是不会走响应式的,但仍是data代理的目标属性
// 一旦操作data触发了响应式,会一并刷新
// 通过原始对象新增属性,不会触发响应式markRaw:标记某个对象,让这个对象永远都不具备响应式。
- 比如有一个巨大的的只读列表,不让其具备响应式是一种性能优化。
