传送门:Vue3中 响应式API ( reactive、ref、toRef、toRefs ) 详解
传送门:Vue3中 响应式 API ( readonly、shallowReadonly、toRaw、markRaw ) 详解
1. shallowReactiv 函数。
reactive() 的浅层作用形式。
只处理对象最外层属性的响应式(浅响应式)。
应用场景: 如果一个对象属性数据,结构较深,但变化时只是外层属性变化。
<template> <div> <div> {{obj1.name}} {{obj1.info.age}} <button @click="obj1Age">obj1Age</button> </div> <div> {{obj2.name}} {{obj2.info.age}} {{obj2.money}} <button @click="obj2Age">obj2Age</button> </div> </div> </template> <script> import { reactive,shallowReactive } from 'vue'; export default { setup(){ const obj1 = reactive({ name:'张三', info:{ age:18 } }) const obj2 = shallowReactive({ name:'李四', money:100, info:{ age:18 } }) const obj1Age = () => { obj1.info.age++; } const obj2Age = () => { //obj2.money++; // 放开注释视图才会进行更新(原因是 shallowRefState 首层 value 发生了改变) obj2.info.age++; console.log(obj1,obj2) } return { obj1, obj2, obj1Age, obj2Age, } } } </script>
先触发 obj1Age 两次,再触发 obj2Age 两次。
2. shallowRef 函数
ref() 的浅层作用形式。
- shallowRef() 函数是浅层响应,只有对 value 整体修改,才能更新到视图层;
- 对属性值的修改是可以的,只是不更新到视图层;
- 对属性值修改之后,可通过 triggerRef() 函数手动更新到视图;
应用场景: 如果一个对象属性数据,后续功能不会修改该对象中的属性,而是新生对象来替换。
<template> <div> <div> {{count}} <button @click="changeCount">changeCount</button> </div> <div> {{count2.num}} <button @click="changeCount2">changeCount2</button> </div> <div> {{count3.num}} <button @click="changeCount3">changeCount3</button> </div> <div> {{count4.num}} <button @click="changeCount4">changeCount4</button> </div> </div> </template> <script> import { shallowRef,triggerRef } from 'vue'; export default { setup(){ const count = shallowRef(0); const count2 = shallowRef({ num:0 }); const count3 = shallowRef({ num:0 }); const count4 = shallowRef({ num:0 }); const changeCount = () => { count.value++ } // 只更改num属性值,不会更新视图 const changeCount2 = () => { count2.value.num++; console.log('count2',count2) } // 整体更新value,会更新视图 const changeCount3 = () => { count3.value = { num: 10} } // 手动更新视图 const changeCount4 = () => { count4.value.num++; triggerRef(count4) } return { count, count2, count3, count4, changeCount, changeCount2, changeCount3, changeCount4, } } } </script>
只触发 changeCount2 两次(未触发其他方法,防止视图更新)
3. triggerRef 函数
强制触发依赖于一个浅层 ref 的副作用,这通常在对浅引用的内部值进行深度变更后使用。
- 手动执行与 shallowRef 关联的任何作用 (effect);
- 配合 shallowRef 用的,并且 shallowRef 传入的是个引用类型;
<template> <div> <div> {{count4.num}} <button @click="changeCount4">changeCount4</button> </div> </div> </template> <script> import { shallowRef,triggerRef } from 'vue'; export default { setup(){ const count4 = shallowRef({ num:0 }); // 手动更新视图 const changeCount4 = () => { count4.value.num++; console.log(count4); triggerRef(count4); } return { count4, changeCount4, } } } </script>
只触发 changeCount4 两次
注意:没有 triggerReactive
4. customRef 函数
创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。
function customRef<T>(factory: CustomRefFactory<T>): Ref<T> type CustomRefFactory<T> = ( track: () => void, trigger: () => void ) => { get: () => T set: (value: T) => void }
customRef() 预期接收一个工厂函数作为参数,这个工厂函数接受 track 和 trigger 两个函数作为参数,并返回一个带有 get 和 set
方法的对象。
一般来说,track() 应该在 get() 方法中调用,而 trigger() 应该在 set() 中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。
<template> <input type="text" v-model="keyWord"> <h3>{{ keyWord }}</h3> </template> <script> import { ref,customRef } from 'vue'; export default { setup(){ function myRef(value,delay = 200){ let timer = null; // return 指代 把自定义的ref函数交出去 return customRef((track,trigger) => { // return 指代 customeRef 形参中的返回体必须返回一个对象,所以必须写return{} return { get(){ console.log('从myRef中读取值',value); // 因为模板<h3>和输入框<input>各读取了一次keyWord属性,所以打印2次 track();// 通知 Vue 追踪 value 的变化 return value }, set(newVal){ // myRef(value)中的形参value代表调用自定义函数传入的参数,而set(newValue)中的newValue代表input输入框修改后的值。 console.log('设置值',newVal) clearTimeout(timer); timer = setTimeout(() => { value = newVal; trigger(); // 通知 Vue 重新解析模板 },delay) } } }) } let keyWord = myRef('hello',300); return { keyWord } } } </script>
效果: