🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] >[success] # api -- toRefs 和 toRef 1. 使用**ES6的解构语法**,对`reactive`返回的对象进行解构获取值,数据都不再是响应式的,想让其保持响应式需要使用`toRefs `或 `toRef` >[info] ## toRef 1. 可以用来为源响应式**对象上的某个 属性**新创建一个 `ref`。然后,`ref `可以被传递,它会保持对其源属性的响应式连接,**这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然**。 ~~~ function toRef<T extends object, K extends keyof T>( object: T, key: K, defaultValue?: T[K] ): ToRef<T[K]> type ToRef<T> = T extends Ref ? T : Ref<T> ~~~ 虽然是单独属性被 ref 包裹,但这类包裹并不是像下面这样单纯对属性单独包裹`ref` ~~~ const state = reactive({ foo: 1, bar: 2 }) // 如果执行foo2.value++ foo和state中的数据不会更新 const foo2 = ref(state.foo) ~~~ 而是像下面代码会**保持对其源属性的响应式连接** ~~~ const state = reactive({ foo: 1, bar: 2 }) const fooRef = toRef(state, 'foo') fooRef.value++ console.log(state.foo) // 2 state.foo++ console.log(fooRef.value) // 3 ~~~ 2. 其实根据上面ts 的类型声明可以看出`toRef`是可以接收任何对象的,如果这个对应的属性不是`ref `会将其转换为`ref `但要注意`toRef` 作用可以响应对象 `Object `,其针对的是某一个属性依旧使其具备响应式,但非响应式的对象不会具备响应式但其属性会被转换为`ref `对象并保持关联 * 这个[案例](https://sfc.vuejs.org/#eNqNVFGOmzAQvYo1PwGJgPiNslV7hf32DyFDlhRsYhtSJeIMVS/RnqxSj9GxwSSQprsgGXvmvTfjB+YKX5om7lqEDWwN1k2VGfzEBWPbfdm5CWPXq8hqfMWi710m8ak7zA2UPqB2rTFSsM95VeZfXzjkb5k4IIeR+ufXz9/ffwycATomnhDTBTNdULfJ3UZoqXNVNsZRyrqRyrArM5I6jUpNI4sUZrkpO+xZoWTNVmTHyqLxm0PvscjailhWzZbVaNomCF3A2DuXQhsmd0f2wpwNGw7n85mDc8JeE+J1rEVIXzaYU0LPmXNHd4nneg9IK+JgoxyIYttYQNN7rK97z5mXKVpBCLJ7cDkIaXtDhnnFuMuq1rbO4XK5cHgApM8Rt5ltUlYYV/IQuDcQjOwwZEnCjGrxA+D0PfSKdr2KaIgtw4FtS0+x3iHH8Yt/cqe3+sS69B3vTqcTh7nCJKno21LCflpzejQFhhq3tffjFvFtLMTdg4ZtMp0IiGA4Eus6a+KjloL+A642HxOaw8Z3w4FOhl1zeDOm0Zsk0UVu/x5HHUt1SGgWq1aYssYYdb3eKXnWqEiYw9ie00go2KFaKxR7VKj+p7mAPuiOm+qh/wvscIlc)中使用`toRef `分别包裹了响应式对象`objReactive ` 和普通对象`obj`, 并且创建了两个事件`change1`和`change` 要注意的是先执行`change1` 发现执行后页面并没有改变`qqq`,这是为`nameRef ` 包裹的是一个非响应式数据。但当执行`change` 时候发现改变`toRef ` 包裹的对象,无论是否是响应式数据其关联的对象都一起形成更改,虽然此时发现`nameRef` 值竟然变了 那是因为`nameRef1` 响应式的将视图改变因此`nameRef`值才被渲染 ~~~html <template> <div> {{nameRef}} </div> <div> {{nameRef1}} </div> <button @click="change"> 触发 </button> <button @click="change1"> 触发1 </button> </template> <script> import { toRef,isRef ,reactive} from 'vue' export default { setup() { const obj = {name:"www"} const objReactive = reactive({name:"www"}) const nameRef = toRef(obj,"name") const nameRef1 = toRef(objReactive,"name") function change(){ nameRef.value = "zzz" nameRef1.value = "zzz" console.log(isRef(nameRef)) // true console.log(isRef(nameRef1)) // true console.log('obj',obj.name) // zzz console.log('objReactive',objReactive.name) // zzz } function change1(){ nameRef.value = "qqq" } return { nameRef, change, nameRef1, change1 } } } </script> ~~~ 3. 对`ref `也想使用`toRef `要注意 `ref `本身其实也是对象,需要对`value `进行取值,[案例](https://sfc.vuejs.org/#eNp9UkFOwzAQ/MrKl6ZSmgiOVYrgC5x9SdNNm5LYlu0EpMhvQHwCXobEM1jbSUpBQpHstWd3ZmfjkT0olQ09si0rLHaqLS3ecQFQHJohBADjKMoOH7F2jmLnApxHPMT73lop4L5qm+ppx1l1KsUROZvqvz7eP1/fYlVM9QAXRf5DkY6m0o2yoajplNQWRrCSdNPG0AqpxrKyzYAU1A5qLTtYUe8rX4EvoeKAddm3VBlbAzBoe5Wsw4X1XyWFsSD3Z9gB8SQ365gIEJHJK6FBO6HMdDWULeksmfNe94IaIuvRcbImlYjAzJOFUmK7vUBeSLaYtfLo6WPKQh7me1GZT5qMaOF9XAuki7E49WuWsNFS5Mt0WcrieDddqbKzkYJ+fqDlE2A4285CnNGE/Zmzk7XKbPPc1JV/MmeTSX3MKcp0L2zTYYam2+y1fDaoiZgz6m3hyOlyQL3RKA6oUf/H+Sv1D+9kyjH3Dai56QU=) ~~~ const obj = ref(1) const nameRef = toRef(obj,'value') ~~~ 4. `const t = toRef(obj,'t') // t.value => undefined `即使源 属性不存在,`toRef `也会返回一个可用的 ref (**在使用可选 prop 时特别有用,可选 prop 并不会被 `toRefs `处理)** >[info] ## toRefs 1. `Vue`为我们提供了一个`toRefs`的函数,可以将`reactive`返回的对象中的属性都转成`ref`(**将一个响应式对象转换为一个普通对象**),这个普通对象其实本质是每个单独的 ref 都是使用[`toRef()`](https://cn.vuejs.org/api/reactivity-utilities.html#toref)创建的 * 转换后 虽然变成普通对象但实际value 还是ref 响应式 ~~~ { name:ref('www') } ~~~ ~~~ const state = reactive({ foo: 1, bar: 2 }) const stateAsRefs = toRefs(state) /* 实际转换后可以理解为一个不同对象每个属性都是响应式的 stateAsRefs 的类型:{ foo: Ref<number>, bar: Ref<number> } */ ~~~ 2. `toRefs`在调用时只会为源对象上可以枚举的属性创建 `ref` 3. 但不能像 `toRef` 为不存在的属性创建 ~~~ function toRefs<T extends object>( object: T ): { [K in keyof T]: ToRef<T[K]> } type ToRef = T extends Ref ? T : Ref<T> ~~~ >[danger] ##### 深层嵌套使用解构案例 1. 如果是深层,此时获取深层中`key `对应`value `例如下面案例中,获取是`name `对应的`{ z: 'info' }",此时" { z: 'info' }`还是`reactive` 对象因此解构了还是具有响应式的 ~~~ html <template> <!-- 可以响应监听的 --> <div>{{ name.z }}</div> </template> <script> import { isReactive, reactive } from 'vue' export default { name: 'App', setup() { // 此时这样去使用es6结构 let { name } = reactive({ name: { z: 'info' } }) // 在1s后改变值 setTimeout(() => { name.z = 'w' console.log(isReactive(name)) // true }, 1000) return { name } }, } </script> ~~~ >[danger] ##### 解构获取非对象值 ~~~ 1.像下面案例解构后获取的仅仅只是 基本类型此时解构后的值已经不在具备响应式,需要使用'toRefs' ~~~ * name 不具有响应式 ~~~ <template> <div>{{ name }}</div> </template> <script> import { reactive } from 'vue' export default { name: 'App', setup() { let { name } = reactive({ name: 'info' }) setTimeout(() => { name = 'w' console.log() }, 1000) return { name } }, } </script> <style></style> ~~~ * 使用toRefs 1. 将 通过直接解构 的`name`值是基本类型,因此做不到**get set 劫持**,导致问题就**不会让其具备响应式**,因此 `toRefs `相当于将这些解构值转换为`ref `让其具有相应劫持效果 2. 要注意`name.value = 'w' ` 赋值为`w `此时 `reactiveInfo `对象中`name `也从`info `变成 `w `,之间建立了 链接,任何一个修改都会引起另外一个变化 ~~~ <template> <div>{{ name }}</div> </template> <script> import { reactive, toRefs } from 'vue' export default { name: 'App', setup() { const reactiveInfo = reactive({ name: 'info' }) const { name } = toRefs(reactiveInfo) setTimeout(() => { name.value = 'w' // 变成了ref 对象需要value 去改动 console.log() }, 1000) return { name } }, } </script> <style></style> ~~~ >[danger] ##### 案例二 1. 可以在不丢失响应性的情况下对返回的对象进行解构/展开 ~~~ function useFeatureX() { const state = reactive({ foo: 1, bar: 2 }) // 操作 state 的逻辑 // 返回时转换为ref return toRefs(state) } export default { setup() { // 可以在不失去响应性的情况下解构 const { foo, bar } = useFeatureX() return { foo, bar } } } ~~~ >[info] ##### 二者的场景 * 初衷: computed、合成函数中,都有可能返回值类型。而值类型的返回,往往容易丢失响应式,在保证不丢失响应式的前提下,把对象解构,方便对象数据分解和扩散 * 前提:针对的是响应式对象(reactive封装的)非普通对象 * 注意: 不创造响应式(那是reactive的事情),只是延续响应式 ~~~ const state = reactive({ x: 25, y: 'Bean' }) //逻辑运行状态,省略N行 。。。。。。 //返回时转换为ref return toRefs(state) ~~~ ~~~ export default { setup() { const {x, y} = demo1() return { x, y } } ~~~ >[info] ## >[info] ## 官网 [## toRef() ## toRefs()](https://cn.vuejs.org/api/reactivity-utilities.html#toref)