[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 源碼解讀
- 開發感悟
- 練手項目