vue3响应式语法中ref和reactive的作用和区别是什么?

128 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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对每个属性添加gettersetter方法,用来对数据的读取和修改进行拦截(getter用来进行依赖收集,setter用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。这是vue2的响应式原理,但是这种方法有以下缺点:

  • 监听对象时只能监听对象本身存在的属性,如果有新添加的属性则无法监听,所以vue设计了其他的方法去操作监听
  • vue2中监听数组如果通过Object.defineProperty的话成本比较高,所以是通过重写数组原型上的七个方法实现的,所以通过this.arr[0] = xxxthis.arr.length = 10是无法触发响应式的

所以在vue3中使用ProxyReflect搭配来代理数据实现响应式的,而vue3中创建响应式数据的方式主要有两种:refreactive,接下来,我们就来分析下这两种方法有什么区别和优缺点。

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是下面的对象:

01.png

发现转换后的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)

02.png

  • reactive创建的对象不能直接进行替换,只能修改其中的属性
const person = reactive({
  name: '高启强',
  company: '强盛集团',
  favourite: '吃老默家的鱼'
})
​
const handleClick = () => {
  person = {
    name: '高启盛',
    company: '强盛集团',
    favourite: 'giegie'
  }
}

上面的代码会报错,因为不支持这样去修改

在介绍ref的时候,我们说ref也可以用来创建响应式对象,那么和reactive有什么区别呢? 其实我们如果用ref来创建响应式对象,底层其实还是调用了reactive来实现的,我们来看下用两者创建响应式对象在控制台打印出来的:

  • reactive创建的对象打印的:

03.png

  • ref创建的对象打印的:

04.png

结论:可以看到,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})