开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情
最近在学习vue3,之前一直用的react,所以在学习的过程中为了加深自己的印象,想写一系列文章来记录学习过程中遇到的一些问题。本篇是对vue3中新引入的ref和reactive的用法和区别来做一个讲解。
前置知识
我们在vue3之前,想要创建响应式数据,必须在data中定义并且return出去
<template>
<h1>{{ title }}</h1>
<button @click="handleClick">点击</button>
</template>
<script>
export default {
data() {
return {
title: "Hello, Vue!"
};
},
methods: {
handleClick() {
this.title = "Hello, World!"
}
}
};
</script>
在vue2中,为了跟踪每个数据的变化,在vue的构造函数中,会依次遍历data中的每个属性,通过Object.defineProperty
对每个属性添加getter
和setter
方法,用来对数据的读取和修改进行拦截(getter
用来进行依赖收集,setter
用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。这是vue2的响应式原理,但是这种方法有以下缺点:
- 监听对象时只能监听对象本身存在的属性,如果有新添加的属性则无法监听,所以vue设计了其他的方法去操作监听
- vue2中监听数组如果通过
Object.defineProperty
的话成本比较高,所以是通过重写数组原型上的七个方法实现的,所以通过this.arr[0] = xxx
和this.arr.length = 10
是无法触发响应式的
所以在vue3中使用Proxy
和Reflect
搭配来代理数据实现响应式的,而vue3中创建响应式数据的方式主要有两种:ref
和reactive
,接下来,我们就来分析下这两种方法有什么区别和优缺点。
ref和reactive
ref的使用
使用ref来创建响应式数据的话,上面的代码就会变为:
<template>
<h1>{{ title }}</h1>
<button @click="handleClick">点击</button>
</template>
<script >
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
const title = ref('Hello Vue!')
const handleClick = () => {
title.value = 'Hello World!'
}
return { title, handleClick }
}
})
</script>
ref
的作用就是把一个基本数据类型的数据变成响应式的,但是其实也可以接受对象。可以看到,我们在修改title
的值的时候,是通过title.value
来修改的,当我们把title
在控制台打印出来的时候发现是title
是下面的对象:
发现转换后的title
是一个RefImpl
类型,这是vue自己在内部的处理,将title
转换成了响应式对象,获取和修改title
的时候都得通过title.value
来操作。
reactive的使用
我们刚介绍ref
用来把一个基本数据类型的数据变成响应式的,那么处理对象和数组的时候就得用reactive
了,你可以简单的将其理解成vue2的选项式API中的整个data。 reactive
的基本用法如下:
<template>
<h1>{{ person.name }}</h1>
<h1>{{ person.company }}</h1>
<h1>{{ person.favourite }}</h1>
<button @click="handleClick">点击</button>
</template>
<script >
import { defineComponent, reactive } from 'vue';
export default defineComponent({
setup() {
const person = reactive({
name: '高启强',
company: '强盛集团',
favourite: '吃老默家的鱼'
})
const handleClick = () => {
person.favourite = '陈书婷'
}
return { person, handleClick }
}
})
</script>
reactive
的作用是将一个对象转变成响应式的,可以看到在修改person
的时候并不需要像ref
创建的变量那样在前面加上.value
。
但是需要注意两点:
- 如果我们尝试用
reactive
创建基本数据类型的响应式时会收到vue发出的警告:
const count = reactive(1)
reactive
创建的对象不能直接进行替换,只能修改其中的属性
const person = reactive({
name: '高启强',
company: '强盛集团',
favourite: '吃老默家的鱼'
})
const handleClick = () => {
person = {
name: '高启盛',
company: '强盛集团',
favourite: 'giegie'
}
}
上面的代码会报错,因为不支持这样去修改
在介绍ref
的时候,我们说ref
也可以用来创建响应式对象,那么和reactive
有什么区别呢? 其实我们如果用ref
来创建响应式对象,底层其实还是调用了reactive
来实现的,我们来看下用两者创建响应式对象在控制台打印出来的:
-
reactive
创建的对象打印的:
-
ref
创建的对象打印的:
结论:可以看到,reactive
返回的对象是一个proxy
对象,而ref
返回的还是一个RefImpl
类型,但是它的value时一个proxy
对象,说明ref
的底层其实还是调用了reactive
的。
但是用ref
来创建响应式对象和reactive
创建的不同之处在于,ref
创建的对象可以整体直接修改:
const person = ref({
name: '高启强',
company: '强盛集团',
favourite: '吃老默家的鱼'
})
const handleClick = () => {
person.value = { // 修改时还是通过value来修改,但是可以整体直接替换
name: '高启盛',
company: '强盛集团',
favourite: 'giegie'
}
}
总结
ref
一般用于创建基础数据类型的数据,在js中读取或者修改都需要使用.value
来操作,但是在模版中使用时则不需要使用.value
reactive
一般用来创建对象类型的数据,不需要使用.value
来操作,但是不能直接对整个对象进行替换,只能单个属性来修改ref
也可以创建响应式对象,但是底层也是调用的reactive
,但是ref
能直接对整个对象进行替换,相当于ref(obj)
等价于reactive({value: obj})
。