Vue3.x — 响应性API

最后更新于:2022-04-02 08:13:26

>[success] # 响应性API ~~~ 1.目前'setup' 的返回对象参数可以直接渲染到模板上,问题是数据不是响应式,想让数据变成响应式 需要使用'3.x' 提供的响应式API ~~~ >[info] ## Ref ~~~ 1.接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property .value 来理解官方这句话'ref() 返回的是value reference (包装对象)并且这个对象value属性指向包装的值让其具备了响应' 2.注意第一条说的是'value' 属性指向了包装对象并且是value具备了响应 2.1.数据操作的是使用是'xxx.value' 2.2.但在模板中简化了操作流程因此可以直接'xxx'不需要调用'value' 属性 3.一般定义基本类型的响应事数据使用'ref' 常见的基本类型'String','Number','BigInt','Boolean','Symbol','Null' 'Undefined' 4.如果你绑定的是对象非上面提出的基本类型他将会调用'reactive ',此时'.value'返回的是一个'proxy' 对象, 关于这一点稍后会详细解释,现在简单看一下如果"const per = ref({name:'z'})" 包裹的是对象相对的你的修改 你需要'per.value.name = "zz" '依然要通过'value' 并且此时'per.value' 是一个'prxoy'对象 5.注意下面案例中其实我们使用const定义参数也间接想表达'ref'包裹的基础类型已经是一个对象(是一个ref对象) ~~~ >[danger] ##### 案例 ~~~ ~~~ >[info] ## reactive ~~~ 1.'reactive' 返回对象的响应式副本,并且他响应转换是'深层'的它影响所有嵌套 property,基于'Proxy'实现, 和 '2.x' 的 Vue.observable()等同 2.解释上面官方的话 2.1.定义对象 时候要使用我并且会返回一个'proxy'对象这个对象具有响应式 2.2.'深层'就是这个对象内部嵌套多深所有嵌套的属性都能影响到举个例子 "const obj =reactive( {name:'ww',per:{age:1100}})" 从obj的name属性到per甚至你的per.age属性都能被我代理了 并且无论你是delete删除还是说新增新的属性都可以被响应到,'reactive'的返回的'proxy'对象可以被响应,如果你操 作原对象不享受这个待遇 2.3.内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的 3.现在可以在进一步理解'ref' 如果传值是对象内部会调用'reactive' 这一点了(注意说的是ref对对象会内部自动调用'reactive') 4.当你从组件中的data()返回一个对象时,它是由reactive()在内部做出反应的 ~~~ >[danger] ##### 案例 ~~~ ~~~ >[danger] reactive 包裹一个对象包含ref的对象 -- Ref Unwrapping(包装对象的自动展开) ~~~ 1.当一个ref作为一个响应式对象的属性被访问或改变时,它会自动打开内部值,这样它的行为就像一个普通的属性 简单的说'当一个包装对象被作为另一个响应式对象的(属性)引用的时候也会被自动展开' 2.理解官方这句话就是 现在我定义了一个ref对象,'const count =ref(0)' =>我将ref对象做一个对象的属性 'const obj = {count}'=>此时我使用是'obj.count.value' 但是如果我用reactive 包裹着含有ref属性的对象即 'const state = reactive(obj)' 他会自动将这个value给打开使用效果变成'state.count'不用再加value属性了 在进一步抽象理解当还有ref属性的对象被reactive包裹实际上效果是'const state = reactive(count:count.value)' const count = ref(0) const obj = reactive({ count }) console.log(obj.count) // 0 obj.count++ console.log(obj.count) // 1 console.log(count.value) // 1 count.value++ console.log(obj.count) // 2 console.log(count.value) // 2 ~~~ ~~~ ~~~ >[danger] ##### 非对象中的属性包含ref对象并且用reactive包裹 ~~~ 1.'当一个包装对象被作为另一个响应式对象的(属性)引用的时候也会被自动展开' 但是当从数组或本地集合类型(如 Map)访问ref时,不会执行展开操作 ~~~ ~~~ const books = reactive([ref('Vue 3 Guide')]) // need .value here 这里依旧需要value console.log(books[0].value) const map = reactive(new Map([['count', ref(0)]])) // need .value here 这里依旧需要value console.log(map.get('count').value) ~~~ >[danger] ##### 上面 reactive 包裹 ref 情况过于复杂我该怎么做 ~~~ 1.以上这些关于包装对象的细节可能会让你觉得有些复杂,但实际使用中你只需要记住一个基本的规则: 只有当你直接以变量的形式引用一个包装对象的时候才会需要用 .value 去取它内部的值 —— 在模版中你甚至 不需要知道它们的存在。 ~~~ >[info] ## 现在对比reactive与ref-细节 ~~~ 1.ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式) 2.如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象 3.ref内部: 通过给value属性添加getter/setter来实现对数据的劫持 4.reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据 5.ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value) ~~~ >[info] ## ref 和 reactive 设计出现 ~~~ 1.我们知道在 JavaScript 中,原始值类型如 string 和 number 是只有值,没有引用的。如果在一个函数中返 回一个字符串变量,接收到这个字符串的代码只会获得一个值,是无法追踪原始变量后续的变化的 ~~~ >[danger] ##### 我应该什么时候去使用这两个Api ~~~ 1.最简单就是基本类型用'ref' ,引用类型用'reactive' ,并且这也是依赖于你的编程习惯,在定义变量的时候 是属于喜欢单独每一项定义还是使用用对象定义 ~~~ * 举个例子单独每一项定义 ~~~ import { ref, onMounted, onUnmounted } from "vue"; export function useMousePosition() { const x = ref(0); const y = ref(0); function update(e) { x.value = e.pageX; y.value = e.pageY; } onMounted(() => { window.addEventListener("mousemove", update); }); onUnmounted(() => { window.removeEventListener("mousemove", update); }); return { x, y }; } ~~~ * 对象定义 ~~~ function useMousePosition() { const pos = reactive({ x: 0, y: 0 }); // ... return toRefs(pos); } ~~~ [参考文章链接](https://www.danvega.dev/blog/2020/02/12/vue3-ref-vs-reactive/)
';