vue3响应式基础

175 阅读4分钟

前言

来段官方响应式对象介绍:

响应式对象其实是 JavaScript Proxy,其行为表现与一般对象相似。不同之处在于 Vue 能够跟踪对响应式对象属性的访问与更改操作。

在vue3中提供了两种创建响应式对象的方式,分别 reactive() 函数,ref()函数,后面会一一进行介绍。接下来让我们先看看 setup 在vue3中的两种写法,如果你已经了解,请跳过。

setup写法

第一种使用setup函数的写法,这种的写法的特点是在组件中定义的响应式数据或者方法需要暴露出去,只有暴露的方法才会被监听器监听到,具体写法如下:

import { reactive } from 'vue'

export default {
  setup() {
    const state = reactive({ count: 0 })

    function increment() {
      state.count++
    }

    // 不要忘记同时暴露 increment 函数
    return {
      state,
      increment
    }
  }
}

第二种写法采用<script setup>的写法,相对第一种简单且好用,关键少写不少代码,推荐使用。

<script  setup>
import { reactive } from 'vue'

const state = reactive({ count: 0 })

function increment() {
  state.count++
}
</script>

reactive()

reactive()函数的作用就是传入一个普通对象,返回一个被 Proxy 包裹的对象。在 vue 中只有代理对象是响应式的,更改原始对象并不会触发视图的更新。下面就看看,这个代理对象长啥样吧!!!

简单数据类型

let h = reactive('hello')
console.log(h);

当对一个简单数据类型使用reactive函数包裹的时候,他返回的仍然是一个简单的数据类型,且提示:“值不能设置响应式。”

image.png

复杂数据类型

let p = reactive({
  name: "张三",
  age: 18
})

console.log(p);

返回代理对象

image.png

reactive()的局限性

  1. 从上面可以得知,使用reactive函数包裹的数据只对对象有效( 数组,对象,map数据集合,set数据集合 ),而对原始数据类型无效。
  2. 由于vue响应式是对于对象的数据进行访问和追踪的,所以当对对象中的属性进行赋值或者解构会失去响应式。如下:
let p = reactive({
  name: "张三",
  age: 18
})
let a = p.age  //对属性进行赋值
a = 20
let { name } = p  //对属性进行解构
name = '奥特曼'

视图不会更新!!!!!

image.png

因为reactive函数的局限性,所以就有了ref函数的出现。

ref()

关于ref()函数官方是这样说的:

ref() 将传入参数的值包装为一个带 .value 属性的 ref 对象:

那么使用ref()返回的简单数据类型和复杂数据类型是什么样的呢?

let h = ref('hello')  //简单数据类型

let car = ref({  //复杂数据类型
  name: '兰博基尼',
  price: '$100'
})
console.log(h);
console.log(car);

控制台展示

image.png

看出区别了吗?对于简单数据类型,通过value可以直接访问,而对于复杂数据类型则返回的proxy对象。

其本质是 ref函数会对当前传入的数据进行类型判断,如果是复杂数据类型,则调用 reactive()函数处理,如果是简单数据类型,则会对value属性的修改和获取进行拦截,在value被get的时候搜集依赖,在value被set的时候触发更新。当然其底层并不是通过 proxy实现的,因为proxy不支持基础数据类型,底层是通过class中取值函数(getter)和存值函数(setter) 来实现的。

//创建响应式对象
class RefImpl {
  constructor(val) {
    // 如果是不是shallow则 存储 reactive proxy 否则存储传入参数
    this._value = _shallow ? value : toReactive(value)
  }
  get value() {
    // track Ref 收集依赖
    trackRefValue(this)
    return this._value;
  }
  set value(newVal) {
    console.log(newVal);
    this._value = newVal;
    // 触发value,更新关联的effect
    triggerRefValue(this, newVal)
  }
};

所以在ref中无论对简单数据类型的数据修改还是复杂数据类型的数据进行修改,页面都会更新

let h = ref('hello')  //简单数据类型

let car = ref({  //复杂数据类型
  name: '兰博基尼',
  price: '$100'
})

setTimeout(() => {
  h.value = '你好'
  car.value.name = '保时捷'
}, 3000);

3秒后的页面

image.png

用ref()解决reactive()的局限性

let car = {
  name: ref('兰博基尼'),
  price: ref('$100')
}
let p = car.price
p.value = '$200'

let { name } = car
name.value = '保时捷'

视图响应式展示:

image.png

总结

  1. 在Vue3中,ref和reactive都是用于在响应式系统中创建响应式数据的方法,但它们之间存在一些区别。
  2. ref是一个函数,用于创建一个包装器对象,将普通的JavaScript数据转换为响应式数据。当你使用ref包装一个值时,它将返回一个具有value属性的响应式对象。你可以通过访问.value属性来读取和修改值。当你修改ref包装的值时,Vue会自动追踪这个变化,并且能够在需要时更新相关的视图。
  3. reactive是一个函数,用于创建一个响应式代理对象。它接受一个普通的JavaScript对象,并返回一个可以追踪对象属性变化的响应式代理。你可以直接访问和修改代理对象的属性,而不需要像ref一样访问.value属性。当你修改代理对象的属性时,Vue同样会自动追踪变化并更新相关的视图。
  4. 总的来说,使用ref适合简单的值类型,而使用reactive适合复杂的对象或需要监听多个属性的情况。另外需要注意的是,由于ref返回的是一个包装器对象,所以在访问或修改值时需要使用.value,而reactive返回的是一个代理对象,可以直接访问和修改属性。