[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)
- 官网给的工具
- 声明vue2 和 vue3
- 指令速览
- Mustache -- 语法
- v-once -- 只渲染一次
- v-text -- 插入文本
- v-html -- 渲染html
- v-pre -- 显示原始的Mustache标签
- v-cloak -- 遮盖
- v-memo(新)-- 缓存指定值
- v-if/v-show -- 条件渲染
- v-for -- 循环
- v-bind -- 知识
- v-bind -- 修饰符
- v-on -- 点击事件
- v-model -- 双向绑定
- 其他基础知识速览
- 快速使用
- 常识知识点
- key -- 作用 (后续要更新)
- computed -- 计算属性
- watch -- 侦听
- 防抖和节流
- vue3 -- 生命周期
- vue-cli 和 vite 项目搭建方法
- vite -- 导入动态图片
- 组件
- 单文件组件 -- SFC
- 组件通信 -- porp
- 组件通信 -- $emit
- 组件通信 -- Provide / Inject
- 组件通信 -- 全局事件总线mitt库
- 插槽 -- slot
- 整体使用案例
- 动态组件 -- is
- keep-alive
- 分包 -- 异步组价
- mixin -- 混入
- v-model-- 组件
- 使用计算属性
- v-model -- 自定义修饰符
- Suspense -- 实验属性
- Teleport -- 指定挂载
- 组件实例 -- $ 属性
- Option API VS Composition API
- Setup -- 组合API 入口
- api -- reactive
- api -- ref
- 使用ref 和 reactive 场景
- api -- toRefs 和 toRef
- api -- readonly
- 判断性 -- API
- 功能性 -- API
- api -- computed
- api -- $ref 使用
- api -- 生命周期
- Provide 和 Inject
- watch
- watchEffect
- watch vs. watchEffect
- 简单使用composition Api
- 响应性语法糖
- css -- 功能
- 修改css -- :deep() 和 var
- Vue3.2 -- 语法
- ts -- vscode 配置
- attrs/emit/props/expose/slots -- 使用
- props -- defineProps
- props -- defineProps Ts
- emit -- defineEmits
- emit -- defineEmits Ts
- $ref -- defineExpose
- slots/attrs -- useSlots() 和 useAttrs()
- 自定义指令
- Vue -- 插件
- Vue2.x 和 Vue3.x 不同点
- $children -- 移除
- v-for 和 ref
- attribute 强制行为
- 按键修饰符
- v-if 和 v-for 优先级
- 组件使用 v-model -- 非兼容
- 组件
- h -- 函数
- jsx -- 编写
- Vue -- Router
- 了解路由和vue搭配
- vueRouter -- 简单实现
- 安装即使用
- 路由懒加载
- router-view
- router-link
- 路由匹配规则
- 404 页面配置
- 路由嵌套
- 路由组件传参
- 路由重定向和别名
- 路由跳转方法
- 命名路由
- 命名视图
- Composition API
- 路由守卫
- 路由元信息
- 路由其他方法 -- 添加/删除/获取
- 服务器配置映射
- 其他
- Vuex -- 状态管理
- Option Api -- VUEX
- composition API -- VUEX
- module -- VUEX
- 刷新后vuex 数据同步
- 小技巧
- Pinia -- 状态管理
- 开始使用
- pinia -- state
- pinia -- getter
- pinia -- action
- pinia -- 插件 ??
- Vue 源码解读
- 开发感悟
- 练手项目