说起响应性api,我们不得不谈到reactive与ref。
一、reactive
- 什么是reactive?
- reactive是Vue3中提供的实现响应式数据的方法
- 在Vue2中响应式数据是通过defineProperty来实现的,而在Vue3中响应式数据是通过ES6的Proxy来实现的
- reactive的注意点:
- reactive参数必须是对象(json/arr)
import { reactive } from 'vue';
export default {
setup(){
/*
*参数必须是对象
*/
let state = reactive({
age: 19
})
return {state}
}
}
- 如果给reactive传递其他对象
默认情况下修改对象,界面不会自动更新
如果想更新,可以通过重新赋值的方式
import { reactive } from 'vue';
export default {
setup(){
/*
*参数必须是对象
*/
// 非json/arr类对象
let state = reactive({
time : new Date()
})
function myFunc(){
// 直接修改之前的数据,页面显示不会刷新
// state.time.setDate(state.time.getDate() + 1);
// 计算之后,直接赋值,页面显示会更新
const newTime = new Date(state.time.getTime());
newTime.setDate(state.time.getDate() + 1);
state.time = newTime
console.log(state)
}
return {state, myFunc}
}
}
二、ref
- 什么是ref?
- ref和reactive一样,也是用来实现响应式数据的方法
- 由于reactive必须传递一个对象,所以导致在企业开发中,如果我们只想让某个变量实现响应式的时候会非常麻烦,所以Vue3就给我们提供了ref方法,实现对简单值的监听
- ref本质:
- ref底层的本质其实还是reactive
系统会自动根据我们给ref传入的值将它转换成reactive
/*
*ref是key为value,value为xx的reactive
*/
ref(xx) -> reactive({value:xx})
- ref注意点
- 在Vue中使用ref的值不用通过value获取
如果是通过ref创建的数据,那么在template中使用的时候不用通过.value来获取,因为Vue会自动帮我们添加.value - JS中使用ref的值必须通过value获取
<template>
<!-- 模版中使用不需要.value,因为Vue已经自动帮我们做了,
如果自己添加.value,实际值相当于age.value.value-->
<p>{{age}}</p>
</template>
<script>
import { ref } from 'vue';
export default {
let age = ref(10)
function myFunc(){
// 不在template中,Vue不会帮我们添加.value,所以必须添加.value
age.value = 11;
}
return {age,myFunc}
}
三、ref和reactive区别
- 虽然说ref的底层是一个reactive,但是其实它们还是有一些区别的。
- ref和reactive的区别:
如果在template里使用的是ref类型的数据,那么Vue会自动帮我们添加.value
如果在template里使用的是reactive类型的数据,那么Vue不会自动帮我们添加.value
- Vue如何决定是否需要自动添加.value的
- Vue在解析数据之前,会自动判断这个数据是否是ref类型的,如果是就自动添加.value,如果不是就不添加
- Vue是如何判断当前的数据是否是ref类型的?
- 通过当前数据的__v_ref的私有属性来判断的
-
如果有这个私有属性,并且取值为true,那么就代表是一个ref类型的数据
- 我们自己也是可以判断数据是否是ref类型,但是我们不能通过访问数据的私有属性__v_ref来判断,但是Vue提供了一个isRef()的对应方法
<template>
<p>{{age}}</p>
</template>
<script>
import { ref , isRef } from 'vue';
export default {
let age = ref(10)
function myFunc(){
age.value = 11;
// 这里返回数据的详细信息,如上图“打印ref对象”所示
console.log(age)
// isRef方法返回一个Boolean值
console.log(isRef(age))
}
return {age,myFunc}
}
- 同理,Vue也提供了isReactive()方法判断是否是reactive类型
四、递归监听
- 递归监听
- 默认情况下,无论是通过ref还是reactive都是递归监听
无论数据嵌套多少层,都可以监听到数据变化
- 递归监听存在的问题
- 如果数据量比较大,非常消耗性能
为什么说它非常消耗性能呢,我们说过,Vue3.0为了实现响应性,它是通过Proxy实现的,所以为了实现递归监听,每一层数据都包装成Proxy对象,也就是数据嵌套的越深,Proxy对象就越多,这本身是非常消耗性能的
<template>
<p>{{state.four}}</p>
</template>
<script>
import { reactive } from 'vue';
export default {
// 多层数据,reactive会进行递归监听
let state = reactive({
one : 'one',
gf:{
two : 'two',
f:{
three : 'three',
s : {
four : 'four'
}
}
}
})
function myFunc(){
state.four = 'haha';
console.log(state);
console.log(state.gf);
console.log(state.gf.f);
console.log(state.gf.f.s);
}
return {state,myFunc}
}
代码执行的结果:
五. 非递归监听
- 既然有递归监听,为什么要非递归监听呢?
- 正如上面说的,递归监听是非常消耗性能的,所以才会有非递归监听来解决这个问题,但并不是说,非递归监听就可以取代递归监听,接下来我们进一步了解非递归监听,我们就知道这两种监听在应用中是相得益彰的,各有利弊
- 什么是非递归监听?
- 非递归监听是只监听数据的第一层,不会监听数据其他层
- 如果创建非递归监听呢?
ref -> shallowRef
reactive -> shallowReactive
如果说ref、reactive是专门创建递归监听数据,那么shallowRef、shallowReactive就是专门创建非递归监听数据 - 非递归监听是怎么实现监听数据变化的?
- 因为只监听第一层数据,所以如果数据第一层不发生变化,我们是监听不到变化的,要想实现响应性,我们必须保证第一层数据值重新赋值(即发生变化)
- 我们打印数据会发现,第一层数据是Proxy对象,其他层数据并不是
<template>
<p>{{state.one}}</p>
</template>
<script>
import { shallowReactive } from 'vue';
export default {
// 多层数据,shallowReactive会进行非递归监听
let state = shallowReactive({
one : 'one',
gf:{
two : 'two',
f:{
three : 'three',
s : {
four : 'four'
}
}
}
})
function myFunc(){
state.one = 'haha';
console.log(state);
console.log(state.gf);
console.log(state.gf.f);
console.log(state.gf.f.s);
}
return {state,myFunc}
}
- 注意点:
如果是通过shallowRef创建数据,那么Vue监听的是.value的变化,value才是真正的第一层数据
- triggerRef()
- 如果我们想直接更改不是第一层数据的值,让页面发生变化,这时候就需要用triggerRef()
- 根据你传入的数据,主动去更新页面
<template>
<p>{{state.four}}</p>
</template>
<script>
import { shallowRef, triggerRef } from 'vue';
export default {
// 多层数据,shallowRef会进行非递归监听
let state = shallowRef({
one : 'one',
gf:{
two : 'two',
f:{
three : 'three',
s : {
four : 'four'
}
}
}
})
function myFunc(){
state.four = 'haha';
triggerRef(state);
}
return {state,myFunc}
}
- 注意点:
Vue3只提供了triggerRef方法,没有提供triggerReactive方法
- 应用场景
一般情况下我们使用 ref和reactive即可
只有在需要监听的数据量比较大的时候,我们才使用shallowRef/shallowReactive
六、shallowRef的本质
之前我们说过ref的本质就是reactive(ref -> reactive)
/*
*ref的本质就是reactive
*/
ref(10) -> reactive({value:10})
/*
* shallowRef的本质就是shallowReactive
*/
shallowRef(10) -> shallowReactive({value:10})
所有如果用shallowRef创建的数据,它监听的是.value的变化
因为底层的本质上value才是第一层
总结:关于响应性API的东西还有很多,接下来我们一起探索Vue 3.0,共同进步。