(6)toRefs
了解完 toRef
后,就很好理解 toRefs
了,其作用就是将传入的对象里所有的属性的值都转化为响应式数据对象,该函数支持一个参数,即 obj
对象
我们来看一下它的基本使用
<script> // 1. 导入 toRefs import {toRefs} from 'vue' export default { setup() { const obj = { name: '前端印象', age: 22, gender: 0 } // 2. 将 obj 对象中属性count的值转化为响应式数据 const state = toRefs(obj) // 3. 打印查看一下 console.log(state) } } </script>
打印结果如下:
返回的是一个对象,对象里包含了每一个包装过后的响应式数据对象
(7)shallowReactive
听这个API的名称就知道,这是一个浅层的 reactive
,难道意思就是原本的 reactive
是深层的呗,没错,这是一个用于性能优化的API
其实将 obj
作为参数传递给 reactive
生成响应式数据对象时,若 obj
的层级不止一层,那么会将每一层都用 Proxy
包装一次,我们来验证一下
<script> import {reactive} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = reactive(obj) console.log(state) console.log(state.first) console.log(state.first.second) } } </script>
来看一下打印结果:
设想一下如果一个对象层级比较深,那么每一层都用 Proxy
包装后,对于性能是非常不友好的
接下来我们再来看看 shallowReactive
<script> import {shallowReactive} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowReactive(obj) console.log(state) console.log(state.first) console.log(state.first.second) } } </script>
来看一下打印结果:
结果非常的明了了,只有第一层被 Proxy
处理了,也就是说只有修改第一层的值时,才会响应式更新,代码如下:
<template> <p>{{ state.a }}</p> <p>{{ state.first.b }}</p> <p>{{ state.first.second.c }}</p> <button @click="change1">改变1</button> <button @click="change2">改变2</button> </template> <script> import {shallowReactive} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowReactive(obj) function change1() { state.a = 7 } function change2() { state.first.b = 8 state.first.second.c = 9 console.log(state); } return {state} } } </script>
来看一下具体过程:
首先我们点击了第二个按钮,改变了第二层的 b
和第三层的 c
,虽然值发生了改变,但是视图却没有进行更新;
当我们点击了第一个按钮,改变了第一层的 a
时,整个视图进行了更新;
由此可说明,shallowReactive
监听了第一层属性的值,一旦发生改变,则更新视图
(8)shallowRef
这是一个浅层的 ref
,与 shallowReactive
一样是拿来做性能优化的
shallowReactive
是监听对象第一层的数据变化用于驱动视图更新,那么 shallowRef
则是监听 .value
的值的变化来更新视图的
我们来看一下具体代码
<template> <p>{{ state.a }}</p> <p>{{ state.first.b }}</p> <p>{{ state.first.second.c }}</p> <button @click="change1">改变1</button> <button @click="change2">改变2</button> </template> <script> import {shallowRef} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowRef(obj) console.log(state); function change1() { // 直接将state.value重新赋值 state.value = { a: 7, first: { b: 8, second: { c: 9 } } } } function change2() { state.value.first.b = 8 state.value.first.second.c = 9 console.log(state); } return {state, change1, change2} } } </script>
首先看一下被 shallowRef
包装过后是怎样的结构
然后再来看看改变其值会有什么变化
我们先点击了第二个按钮,发现数据确实被改变了,但是视图并没随之更新;
于是点击了第一个按钮,即将整个 .value
重新赋值了,视图就立马更新了
这么一看,未免也太过麻烦了,改个数据还要重新赋值,不要担心,此时我们可以用到另一个API,叫做 triggerRef
,调用它就可以立马更新视图,其接收一个参数 state
,即需要更新的 ref
对象
我们来使用一下
<template> <p>{{ state.a }}</p> <p>{{ state.first.b }}</p> <p>{{ state.first.second.c }}</p> <button @click="change">改变</button> </template> <script> import {shallowRef, triggerRef} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowRef(obj) console.log(state); function change() { state.value.first.b = 8 state.value.first.second.c = 9 // 修改值后立即驱动视图更新 triggerRef(state) console.log(state); } return {state, change} } } </script>
我们来看一下具体过程
可以看到,我们没有给 .value
重新赋值,只是在修改值后,调用了 triggerRef
就实现了视图的更新
(9)toRaw
toRaw
方法是用于获取 ref
或 reactive
对象的原始数据的
先来看一段代码
<template> <p>{{ state.name }}</p> <p>{{ state.age }}</p> <button @click="change">改变</button> </template> <script> import {reactive} from 'vue' export default { setup() { const obj = { name: '前端印象', age: 22 } const state = reactive(obj) function change() { state.age = 90 console.log(obj); // 打印原始数据obj console.log(state); // 打印 reactive对象 } return {state, change} } } </script>
来看看具体过程
我们改变了 reactive
对象中的数据,于是看到原始数据 obj
和被 reactive
包装过的对象的值都发生了变化,由此我们可以看出,这两者是一个引用关系
那么此时我们就想了,那如果直接改变原始数据 obj
的值,会怎么样呢?答案是:reactive
的值也会跟着改变,但是视图不会更新
由此可见,当我们想修改数据,但不想让视图更新时,可以选择直接修改原始数据上的值,因此需要先获取到原始数据,我们可以使用 Vue3 提供的 toRaw
方法
toRaw
接收一个参数,即 ref
对象或 reactive
对象
<script> import {reactive, toRaw} from 'vue' export default { setup() { const obj = { name: '前端印象', age: 22 } const state = reactive(obj) const raw = toRaw(state) console.log(obj === raw) // true } } </script>
上述代码就证明了 toRaw
方法从 reactive
对象中获取到的是原始数据,因此我们就可以很方便的通过修改原始数据的值而不更新视图来做一些性能优化了
注意: 补充一句,当
toRaw
方法接收的参数是ref
对象时,需要加上.value
才能获取到原始数据对象
(10)markRaw
markRaw
方法可以将原始数据标记为非响应式的,即使用 ref
或 reactive
将其包装,仍无法实现数据响应式,其接收一个参数,即原始数据,并返回被标记后的数据
我们来看一下代码
<template> <p>{{ state.name }}</p> <p>{{ state.age }}</p> <button @click="change">改变</button> </template> <script> import {reactive, markRaw} from 'vue' export default { setup() { const obj = { name: '前端印象', age: 22 } // 通过markRaw标记原始数据obj, 使其数据更新不再被追踪 const raw = markRaw(obj) // 试图用reactive包装raw, 使其变成响应式数据 const state = reactive(raw) function change() { state.age = 90 console.log(state); } return {state, change} } } </script>
我们来看一下在被 markRaw
方法处理过后的数据是否还能被 reactive
包装成响应式数据
从图中可以看到,即使我们修改了值也不会更新视图了,即没有实现数据响应式