>[success] # reactive
1. `reactive `函數創建一個響應式**對象或數組**,返回對象的響應式副本,**并且他響應轉換是深層的它影響所有嵌套屬性**,基于[JavaScript Proxy](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy)實現,和` 2.x`的 `Vue.observable()`等同,
* **定義對象** 時候使用`reactive ` 包裹并且返回一個`proxy`對象**這個對象具有響應式**,注: **僅對對象類型有效(對象、數組和`Map`、`Set`這樣的[集合類型](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects#%E4%BD%BF%E7%94%A8%E9%94%AE%E7%9A%84%E9%9B%86%E5%90%88%E5%AF%B9%E8%B1%A1)),而對`string`、`number`和`boolean`這樣的[原始類型](https://developer.mozilla.org/zh-CN/docs/Glossary/Primitive)無效**。
* **深層**這個對象內部嵌套多深所有嵌套的屬性都能轉換為**響應式的**
* 內部基于 `ES6` 的 `Proxy `實現,**通過代理對象操作源對象內部數據都是響應式的**
* **為保證訪問代理的一致性,對同一個原始對象調用`reactive()`會總是返回同樣的代理對象,而對一個已存在的代理對象調用`reactive()`會返回其本身**
* `Vue `的響應式系統是通過屬性訪問進行追蹤的,因此我們必須始終保持對該響應式對象的相同引用,因為使用了,`Proxy`代理在 `get `和 `set `方法做了劫持 因此能在數據變化的時候可以重新渲染`template `模板,**當我們使用reactive函數處理我們的數據之后,數據再次被使用時就會進行`依賴收集`**,**當數據發生改變時,所有收集到的依賴都是進行對應的`響應式操作`(比如更新界面)**
2. 官網使用`Proxy ` 收集`reactive `的一個縮寫版本源碼實現
~~~
const dinner = {
meal: 'tacos'
}
const handler = {
get(target, property, receiver) {
track(target, property)
return Reflect.get(...arguments)
},
set(target, property, value, receiver) {
trigger(target, property)
return Reflect.set(...arguments)
}
}
const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)
~~~
>[info] ## 案例
1. [案例](https://sfc.vuejs.org/#eNqNVslu00AYfpWRL25EG8O1SiIQL8CBG+bgJhMIJLY1dgI0ilT2LYECZWtCIXBoRddUQNONvozHcU+8Av+Md8eFOlI0M/8333z/Zk9TuKDr2UYdC9NCziiSim6igqxWarpGTNREBCtFs9LAqIXKRKshEaCirMoqvs0RJVxW6lVAyiqCR1VqeBqJwClOMhRbM7BZ1ycyPoQ9koSKmmqYaEYx8OU7Okb54KQJ8ZyYQfTznt1bGw326dLz0eKD0f4rurFof9g8XvjobG7Cik8WkrqMRa2umkDXRA2lWgcx51ArdjCdO7D2v43mH9lP5umbDt1boAcvkyw60W7fuehRBco4dybExnjtnQEd3HOpk3QljPVLjDLK1my5VHGWNx3rsOd8XQ0CT9sPnW+79OGO3Xsa6KWbu86gD64kT1JKJX6Qrx3ini9EQ+9DtSrOVrVrE6GnEcfYc+ZMaMryWIZ2iGiKavriMyg67v88/vTVGnasg0W7+8N+t0W7X6zhHrMuPwcA3f1lb/fBG/efvm67W5yjrtNv22+37M4GfbZqt++mOHdqv3iuXN1jjkVsMZ9S8+qVHs+uvdaH0qODJXtuOS3HF0yTcHH5QkyaJ58w4xWxrGnipDijEPFqFBQUSdYFwv+J5itnrwJCFmZnZ2UhigpzFs+W6f7An6D+Msg5WqDdJfAIGssazlnD716SeHXBOuIH/jlo0437kKeolW/pjLrD0frTSDuGbVjTFcL6OjUYsSyhfD4fEZ5hUS8rVSOSHJ6J48cdVk28H6zfn+z3y9bh0Whhhe5s0/UPtLcCOtz+cyUy3b0VOt/2fOOL1vCZs3UfdkUDAaXqxaI7BHzAc5LsxBshzQGT1DGrJD/yPPYgx9Xi0v9bC8Tcefyd7gz+qyJ69AlSog0bqXQC72aiJvvI7zN4hSeWw3dLwhYemTAEZZuyztolsezVzZjYFuBgmJPcbxR8omBi4ppeVUwMM47NlSoNf+xPm82INrfnUauVk1KhgdgQ4WcvN1M3TU1F54vVSvFmXhZ8B2ShwIY5yQWEpCl7fO+Egjca34XGNsXCDltj81MSRPambfOdzUlBSIVJwb0ETNUUPXvD0FS4IfA6kT2DIQvTfuXIAtwL2FwWrpumbkxLklEusnvFDSOrkWsSjLIEDq7UcBYbtakZot0yMAFiWfBKgHNIsNjAZIpgtYQJhuiezJmAjvEyWqiZltD6C2yqZQQ=)將`count `對象通過`reactive `包裹變成響應式,當操作 `addProxyCount `方法(該方法操作響應式對象`proxyCount`)數據改變頁面也會改變,當操作`addCount `方法時(操作是`target `對象)頁面數據不會發生改變因為`count `并沒有和頁面進行數據劫持
2. 如果此時先點擊`addCount`**9次** 在點擊`addProxyCount `頁面會**顯示為11**,因為`count `被`reactive `代理形成`proxyCount `,實際操作`proxyCount ` 也是從最新的`count `中獲取,只是count 沒有和頁面形成響應式因此沒變化
`new Proxy(count,{get(target,key){ ... },set(target, property, value, receiver){...}})`
3. 在`vue2 `版本,新增的`key `猶豫并沒有被響應劫持因此需要使用特殊方式例如`set `將值塞回響應劫持對象中,在`vue3`更換`proxy `后就不必這樣了
~~~html
<script >
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
// const baseType = reactive('1') 基本類型的綁定是錯誤的
const count = { value: 1 }
// 值代理成響應式
const proxyCount = reactive(count)
// 深層代理
const deepProxy = reactive({})
// 操作被reactive 包裹具有響應式對象值
const addProxyCount = () => {
console.log(proxyCount)
++proxyCount.value
}
// 操作原值頁面不會更新因為原始值并沒有沒有和頁面進行數據劫持
const addCount = () => {
console.log(count.value)
++count.value
}
// 綁定深層次的屬性
const deepAttr = ()=>{
const arr = ['foo','bar']
deepProxy.arr = arr
deepProxy.arr[0] = "zzz"
proxyCount
}
// reactive() 返回的是一個原始對象的 Proxy,它和原始對象是不相等的
const compare = ()=>{
console.log(count === proxyCount) // false
// 重新包裹依舊使用已存在的代理對象,在同一個對象上調用 reactive() 會返回相同的代理
console.log(reactive(count) === proxyCount) // true
// 在一個代理上調用 reactive() 會返回它自己
console.log(reactive(proxyCount) === proxyCount) // true
}
return {
addCount,
addProxyCount,
proxyCount,
deepProxy,
deepAttr,
compare
}
},
}
</script>
<template>
<div>
<div>{{ proxyCount.value }}</div>
<div>{{deepProxy}}</div>
<button @click="deepAttr">deep</button>
<button @click="compare">compare</button>
<button @click="addProxyCount">addProxyCount</button>
<button @click="addCount">addCount</button>
</div>
</template>
~~~
>[info] ## ref 作為屬性會自動解包
1. `reactiv `對深層的嵌套屬性都會進行響應式代理,如果使用`ref` 包裹的值最后也是會成為一個含有`value `屬性的對象,這類對象在`reactiv `屬性中使用依舊如此保持響應性。但不同的是**深層地解包任何[ref](https://cn.vuejs.org/api/reactivity-core.html#ref)屬性**
2. **當訪問到某個響應式`數組`或`Map`這樣的原生集合類型中的 `ref 元素時`,不會執行 ref 的解包**
[案例](https://sfc.vuejs.org/#eNp9VN2O0kAUfpXj3FASaGWvDIGNPgBhb7xiuCg4IEinzXTKrmma4IUaE1w02WyMP1kT467xwsQrdlcSX4bu4pWv4Jm2QIHGpGl7zjfn7/tmxicPHEcfeoyUScVti54jYZ/ynuXYQoIPgnUKgplt2RsyCKAjbAtyuDxHOeXsKFr1iHVMbyB9ygFcJj1Hy0f/AG2buxIcYR89hSrmivNofpBP41jjYLWko5UywXqrn+A+Ny1WpqRESXaemulAspazQ0BTazQoMbuMkgLG7VHSbOaTUMOA+eXrxcWXcPw8nczj656SthsqY66UyzfXseiCrGDHbD/BWC1f3U+4gJgHXRzEvUXpt6B6CsKBt9BaCsWhlqgqaQ+YPrC72rJE0mAmWP8fWFuBy68i6PePm5Or8M2xmjYczeZXZ/PZ+8X07PbTKJy8C8enf2fj8MPn+eV1zMX8+sXNq9H811ek9vbkmz40Bx6DxfE0nJxifAYhe9tM6EplBChBubaJ0HGbaTkUNKf0vEdJ0nWwoQsWV23uqKO0qaaVSfOQ6N6424y7zqtsJfjzcbQ4f7Z4+T2c/oz8GwUF7nrBV0LH8hc2ul5ZSwV3HCj4jg9lXqfBnKmq+MKH8ooRH1s8tWhIZjkDUzK0ACotT0qbw/32oIcjUxI3RkmEAjyMt+maoYoRR2RHb8RGkRsnZzP4TrG4uDgPJ2+Lxcjh+8uZggCMlIljK8/agTMHOFnFWM1CCiS+kIqW6eh91+Z4W0Vs0wRwKSlDwj8leD8pm5LHUjpu2TDcTlvdcX1Xt0XXwD9deFz2LKYz1yq2hH3oMoGJ452mmCXBP8TU03U=)
~~~html
<script >
import { ref,reactive } from 'vue'
export default{
setup(){
const proxy = reactive({})
const refProxy = ref(1)
const refProxyObj = ref({name:"1"})
const refProxyMap = ref(new Map([["age","12"]]))
// 不解包
const unProxy = reactive([ref('1')])
// ref 解包
const unpack = ()=>{
proxy.rP = refProxy
proxy.rO = refProxyObj
proxy.rM = refProxyMap
console.log(proxy.rP)
console.log(proxy.rO)
console.log(proxy.rM)
// 修改后ref 值也會跟著變化,因為解包了所以不用.value 獲取值
proxy.rP = 2
proxy.rO.name = "2"
proxy.rM.set('age',"18")
}
// ref 不會解包
const pack=()=>{
console.log(unProxy[0].value) // 1 需要自己value 因為元素為ref
}
return{
unpack,
proxy,
refProxy,
refProxyObj,
refProxyMap,
pack
}
}
}
</script>
<template>
<button @click="unpack">
Unpack 解包
</button>
<button @click="pack">
pack 不解包
</button>
<!--觸發-->
{{refProxy}} / {{refProxyObj}} /{{refProxyMap}}
</template>
~~~
>[info] ## 解構后會失去響應式
1. `reactive ` 在解構后會失去其響應式的功能 但不絕對
2. 如果是深層,此時獲取深層中`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>
~~~
>[info] ## 官網
[## reactive()](https://cn.vuejs.org/api/reactivity-core.html#reactive)
[## 聲明響應式狀態](https://cn.vuejs.org/guide/essentials/reactivity-fundamentals.html#declaring-reactive-state)
- 官網給的工具
- 聲明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 源碼解讀
- 開發感悟
- 練手項目