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] ##### 案例
~~~
';
{{str}}
~~~
>[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] ##### 案例
~~~
{{proxyObj.count}}
{{proxyObj.name}}
~~~
>[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
~~~
~~~
{{ count }}--{{rcount.count}}
~~~
>[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/)