Vue中你一定使用过的两个API:ref和reactive

140 阅读4分钟

在学习vue或项目开发中都少不了ref和reactive的身影,他们是一对响应式API,是vue数据驱动视图的关键。

什么是响应式?

我们先拿出vue.js官网的例子:

image.png
A2单元格的值是根据A0+A1相加得到的,当你试图修改A0或A1时,会发现A2也自动更新了。

A0或A1的改动使A2自动更新->这就行为就是响应式,也叫数据驱动视图

ref()reactive()作为vue中声明响应式的状态的两个重要API,它们的表现形式、使用方式和使用场景都不同,下面我们进入正题。

Object.defineProperty()

首先我们了解下在标准的javascript中如何检测数据的变化。

在标准javascript中,检测普通变量的访问或修改是行不通的,但我们可以借助Object.defineProperty()这个静态方法并根据其内部定义的gettersetter来拦截属性的访问和修改。 对Object.defineProperty不了解的小伙伴可以访问-> Object.defineProperty() - JavaScript | MDN
具体代码如下:

const obj = {
    name: "张三",
    age: 18
};
Object.defineProperty(obj, "age", {
    getter: (){
        console.log('读取到年龄:' + obj.age)
        return obj.age;
    },
    setter: (newVal) {
        console.log('拦截到修改操作,新的年龄为:' + newVal)
        obj.age = newVal;
    }
});

console.log(obj.age); //会触发上边getter 
obj.age = 20;
console.log(obj.age) //上边的setter会触发
本质上vue为我们提供的ref和reactive在底层也是做了同以上相似的拦截操作。

ref()

vue官方是推荐我们使用ref()来定义响应式变量的。

ref的使用方式

使用ref()来声明一个响应式变量并给予其默认值,如

const name = ref("");

当我们访问变量时要在变量后边加上.value,比如你要访问name属性。

console.log(name.value);

为什么vue能拦截到属性的访问和修改呢?这里的.value其实就给予了vue一个机会来检测ref何时被访问和修改。

ref()优势

  • 可以持有任何类型的值,包括变量、深层嵌套对象、数组或者javascript内置数据结构,如Map
  • 可以将用ref定义的变量或对象传递给函数,仍然保留对最新值和响应式连接的访问。

reactive()

另一种声明响应式状态的方式,使用reactive()API。
我们可以这样理解:

ref()是将内部值包装在特殊对象中,需要使用.value访问。
reactive()则是对象本身具有响应式,可以直接访问属性

reactive的使用方式

当我们访问一个reactive()声明的对象:

const obj = reactive({
    name: "yy"
    age: 11,
});
console.log(obj.name); //yy
console.log(obj.age); //11

可以像访问原始对象一样的方式来访问响应式对象中的属性。

reactive的局限性

  • 有限的值类型:只能用于对象类型(对象、数组和Map、Set集合类型),不能持有string、number、boolean这样的原始类型。
  • 不能替换整个对象:Vue的响应式跟踪是根据属性访问实现的,所以我们必须保证响应式对象的相同引用。即不能轻易地“替换”响应式对象,不然响应式连接将断开:
const state = reactive({count: 11});
//上边的({ count: 11 })引用将不再被追踪
//(响应式连接已丢失)!
state = reactive{count: 22};
  • 解构不友好;当我们将响应式对象中某个属性解构为本地变量时,或者将该属性传递给函数时,将丢失响应式连接:
const info = reactive({age: 18});
//解构为本地变量age,已经与info.age断开连接
const {age} = info;
//不会影响原先的info
age++;

//该函数接收到一个普通的数组
//并且无法追踪info.age的变化
//错误写法
handleInfoFun(info.age);

//必须传入整个对象以保证响应式
//正确写法
handleInfoFunc(info)

通过上边提及到reactive的一些限制,我们会更倾向于使用ref()作为声明响应式状态的主要API。

总结

ref更为全面和灵活,无需考虑类型和响应式连接丢失等问题

如果你在项目某个地方需要声明响应式对象。但是不确定用哪个,那就使用ref()。

reactive存在局限性,需要在合适的场景下再使用

如果文章对你有帮助的话,请点个赞呀,也欢迎各位友友评论,我们一起进步~