🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
>[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)