承上启下
在上一节 ref() 函数中,我们大致理解了 ref() 函数的作用是用来将数据转化为响应式的。但是对于基本类型和引用类型,Vue3底层做的转换不一致:对于基本类型,Vue3 通过 ref() 函数将变量转化为了 RefImpl引用对象从而实现响应式,对于引用类型,Vue3 通过ref()函数将变量转化为 RefImpl引用对象,采用基于 ES6的Proxy 的 reactive 函数,对于变量值实现响应式(包含深层响应)。
reactive 定义一个基础类型的响应式数据
定义一个对象内类型的响应式数据( 基本类型只能使用 ref() 函数转化为响应式 ),我们可以用 reactive 定义一个基本类型的值来试试
javascript
<template>
<p>姓名:{{ a }}</p>
<button @click="change">点击修改</button>
</template>
<script>
import { reactive } from 'vue'
export default {
name: "App",
setup() {
let a = reactive(666)
console.log(a)
function change() {
a = 1234
}
return {
a,
change
};
},
};
</script>
我们可以看到,虽然页面上展示了正确数据,但是在控制台上Vue已经报了警告,并不建议我们这样做,此时我们点击按钮改变数据,发现数据已经改变了,但是页面并没有更新,这表示当前属性a,并不是一个响应式数据,这也表示了为什么Vue3 不建议使用reactive 来转化基础数据
reactive 定义一个基础类型的响应式
reactive 定义一个对象类型的响应式数据
上面案例表明 reactive 函数无法将基础数据类型转化为响应式数据,那我们现在来试一试 reactive 函数是否能将 引用类型数据转化为响应式。
javascript
<template>
<p>姓名:{{ userInfo.name }}</p>
<p>年龄:{{ userInfo.age }}</p>
<p>工作:{{ userInfo.work }}</p>
<button @click="change">点击修改</button>
</template>
<script>
import { reactive } from 'vue'
export default {
name: "App",
setup() {
let userInfo = reactive({
name: 'al',
age:'29',
work:'前端'
})
console.log(userInfo,'userInfo')
function change() {
userInfo.name = "汤圆仔";
console.log(userInfo);
}
return {
userInfo,
change
};
},
};
</script>
此时页面展示正确,控制台打印当前经过转化为响应式的数据。是一个 Proxy 代理对象
点击按钮修改数据后,页面展示正确,控制台上打印的 Proxy 代理对象中 name 属性值夜变化了
此时我们注意到,修改数据时,我们并没有像 ref() 函数转化响应式对象时,通过 xxx.value 来修改属性值,而是直接通过 xxx.xxx 进行修改的。
reactive 定义一个数组类型的响应式数据
javascript
<template>
<p>工作:{{ hobby }}</p>
<button @click="change">点击修改</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
setup() {
let hobby = reactive(['抽烟','喝酒','烫头'])
console.log(hobby);
function change() {
hobby[0] = '学习'
console.log(hobby,'hobby');
}
return {
hobby,
change,
};
},
};
</script>
控制台上打印 转换过后 hobby 属性,我们发现也是一个 Proxy 代理对象,但还是一个 Array,
我们通过数组下标改变数据,点击按钮之后发现页面上数据真的修改了,在Vue2中这是行不通的:Vue2中不能通过数组下标直接修改数组,不能通过 length属性 直接设置数组长度
但是还是能证明一点,reactive() 函数能将数组数据转化为响应式数据
reactive 定义一个深层嵌套对象类型的响应式数据
javascript
<template>
<p>工作:{{ userInfo.test.a.b.c }}</p>
<button @click="change">点击修改</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
setup() {
let userInfo = reactive({
test: {
a: {
b: {
c: 666
}
}
}
});
function change() {
userInfo.test.a.b.c = 999
console.log(userInfo,'userInfo');
}
return {
userInfo,
change,
};
},
};
</script>
点击按钮后,数据修改,同时页面同步更新。深层嵌套数据也被转化为 Proxy代理对象
这能证明 reactive() 函数也能将深层嵌套对象转化为响应式数据
总结
作用:定义一个 对象类型的响应式数据( 基本类型还请使用 ref()函数转化 )
语法:let 代理对象 = reactive(源对象)。接收一个对象或数组,返回一个代理对象
深度:reactive() 定义的响应式数据时深层次的,嵌套的对象或数组中的对象都能响应
底层:内部基于 ES6 的 Proxy实现,通过代理对象操作源对象内部数据进行操作
代理对象 Proxy 和 源对象 并不全等,只有代理对象是响应式的,更改原始对象不会触发更新。所以Vue3 推荐只使用 代理对象进行数据操作
不足:
-
有限的值类型 :它只能用于对象类型 (对象、数组和如
Map
、Set
这样的集合类型)。它不能持有如string
、number
或boolean
这样的原始类型。 -
不能替换整个对象: 由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用,如果替换整个对象,那么初始的响应式关联会丢失
javascriptlet state = reactive({ count: 0 }) // 上面的 ({ count: 0 }) 引用将不再被追踪 // (响应性连接已丢失!) state = reactive({ count: 1 })
-
对于解构操作不友好 :当我们将响应式对象的基础类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:
javascript
const state = reactive({ count: 0 })
// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收到的是一个普通的数字
// 并且无法追踪 state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count)