在学习vue或项目开发中都少不了ref和reactive的身影,他们是一对响应式API,是vue数据驱动视图的关键。
什么是响应式?
我们先拿出vue.js官网的例子:
A2单元格的值是根据A0+A1相加得到的,当你试图修改A0或A1时,会发现A2也自动更新了。
A0或A1的改动使A2自动更新->这就行为就是响应式,也叫数据驱动视图
ref()和reactive()作为vue中声明响应式的状态的两个重要API,它们的表现形式、使用方式和使用场景都不同,下面我们进入正题。
Object.defineProperty()
首先我们了解下在标准的javascript中如何检测数据的变化。
在标准javascript中,检测普通变量的访问或修改是行不通的,但我们可以借助Object.defineProperty()这个静态方法并根据其内部定义的getter和setter来拦截属性的访问和修改。
对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存在局限性,需要在合适的场景下再使用
如果文章对你有帮助的话,请点个赞呀,也欢迎各位友友评论,我们一起进步~