传送门:Vue3中 响应式 API ( reactive、ref、toRef、toRefs ) 详解
传送门:Vue3中 响应式 API( shallowReactive、shallowRef、triggerRef 、customRef )详解
1. readonly 函数
接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。
要避免深层级的转换行为,请使用 shallowReadonly() 作替代。
<template> <div> <div> {{count}} <button @click="changeCount">changeCount</button> </div> <div> {{obj1.name}} {{obj1.info.age}} <button @click="obj1Age">obj1Age</button> </div> </div> </template> <script> import { reactive,readonly, ref } from 'vue'; export default { setup(){ let count = ref(0); let obj1 = reactive({ name:'张三', info:{ age:18 } }) count = readonly(count); obj1 = readonly(obj1); const changeCount = () => { count.value++; console.log(count) } const obj1Age = () => { obj1.name = '李四'; obj1.info.age++; console.log(obj1) } return { count, changeCount, obj1, obj1Age, } } } </script>
分别触发 changeCount、obj1Age 一次
2. shallowReadonly 函数
readonly() 的浅层作用形式
和 readonly() 不同,这里没有深层级的转换:只有根层级的属性变为了只读。属性的值都会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。
<template> <div> <div> {{count}} <button @click="changeCount">changeCount</button> </div> <div> {{obj1.name}} {{obj1.info.age}} <button @click="obj1Age">obj1Age</button> </div> </div> </template> <script> import { reactive,shallowReadonly, ref } from 'vue'; export default { setup(){ let count = ref(0); let obj1 = reactive({ name:'张三', info:{ age:18 } }) count = shallowReadonly(count); obj1 = shallowReadonly(obj1); const changeCount = () => { count.value++; console.log(count) } const obj1Age = () => { obj1.name = '李四'; obj1.info.age++; console.log(obj1) } return { count, changeCount, obj1, obj1Age, } } } </script>
分别触发 changeCount、obj1Age 一次
3. toRaw 函数
根据一个 Vue 创建的代理返回其原始对象。
toRaw() 可以返回由 reactive()、readonly()、shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象。
这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。
<template> <div> <div> {{obj.name}} {{obj.info.age}} <button @click="objToRaw">objToRaw</button> </div> </div> </template> <script> import { reactive,toRaw } from 'vue'; export default { setup(){ let obj = reactive({ name:'张三', info:{ age:18 } }) const objToRaw = () => { let objRaw = toRaw(obj) objRaw.info.age++ console.log(obj,objRaw) } return { obj, objToRaw, } } } </script>
触发 objToRaw 两次
4. markRaw 函数
将一个对象标记为不可被转为代理。返回该对象本身。
作用:标记一个对象,使其永远不会再成为响应式对象;
应用场景:
- 有些值不应被设置为响应式的,如:复杂的第三方类库等;
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可提高性能;
<template> <div> <div>姓名:{{obj.name}} </div> <div>年龄:{{obj.info.age}} </div> <div v-if="obj.car">座驾:{{obj.car}} </div> <div v-if="obj.address">地址:{{obj.address}} </div> <button @click="updateObj">updateObj</button> <button @click="changeAddress">changeAddress</button> <button @click="changeCar">changeCar</button> </div> </template> <script> import { reactive,markRaw } from 'vue'; export default { setup(){ let obj = reactive({ name:'张三', info:{ age:18 } }) const updateObj = () => { const address = { province:'湖北',city:'武汉' }; obj.address = address; // obj 配置了 Proxy 代理,因此捕获到对 obj 任何属性的操作且都是响应式的; const car = { brand:'宝马',price:30 }; obj.car = markRaw(car); // car内部属性值已经改了,只是没有响应式更新。它和readonly不同,readonly是压根不让修改 } function changeAddress(){ obj.address.province = '江苏'; obj.address.city = '南京'; console.log(obj) } function changeCar(){ obj.car.brand = 'BMW', obj.car.price+= 1 console.log(obj) }; return { obj, updateObj, changeCar, changeAddress, } } } </script>
依次触发 updateObj、changeAddress、changeCar 一次,效果如下:
(若先触发 changeCar 再触发 changeAddress ,car 的页面信息会进行更新,因为 address 的改变带动了页面的更新 )