什么是响应式?意思是在改变数据的时候,视图会跟着更新。
一、Vue.js响应式的例子
<template>
<div id="app">
<span @click="setHello">{{ greeting }}</span>
<span>{{ greeting }}</span>
<span>{{ greeting }}</span>
<span>{{ greeting }}</span>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
greeting: "Hello, my friend!",
};
},
methods: {
setHello() {
this.greeting = "你好呀,我的朋友!";
},
},
};
</script>
运行上述代码,你会发现我们通过触发更改数据的方法,视图自动更新,并且当多处引用greeting这个变量时,视图都会自动更新。这就是响应式。
二、响应式原理
- Vue2.x 是使用
Object.defineProperty(),来实现对对象的监听。 - Vue3.x 版本之后就改用
Proxy实现。
因为时间关系和本渣渣知识水平有限,本篇文章暂时只讨论Object.defineProperty()
三、Vue2
前面提到Vue2的响应式是基于Object.defineProperty()来实现的,这是因为当你把一个普通的JavaScript的对象传给Vue实例中的data选项,Vue将遍历此对象的所有的属性,并使用Object.definePropety把这些属性全部转为getter/setter。
举个栗子
function reactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
console.log("我被读了,我要不要做点什么好?");
return val;
},
set(newVal) {
if (val === newVal) {
return;
}
val = newVal;
console.log("数据被改变了!");
},
});
}
const data = {
name: "Eileen",
age: 18,
};
// 对data上的所有属性进行绑定
Object.keys(data).forEach((key) => reactive(data, key, data[key]));
console.log(data.name); // 控制台输出 <我被读了,我要不要做点什么好?> <Eileen>
console.log(data.age); // 控制台输出 <我被读了,我要不要做点什么好?> <18>
data.name = "Vue"; // 控制台输出 <数据被改变了!>
但Object.defineProperty()存在一些问题,使得Vue 无法检测到 property 的添加或删除。
data.hobby = 'skiing';
console.log(data.hobby); // 控制台输出 <skiing>
这时我们应该可以发现没有触发data对象的get和set。所以弊端就是:Object.defineProperty 只对初始对象里的属性有监听作用,而对新增的属性无效。这也是为什么Vue2中对象新增属性的修改需要使用Vue.set或者vm.$set方法来设值的原因。
另一个问题来了,如果data里面有数组怎么办? 对于数组,尤雨溪篡改了7个API方便你对数组进行增删,这7个API会自动处理监听和代理,并更新UI。这7个API分别是
push(),pop(),shift(),unshift(),splice(),sort(),reverse()。具体参考文档中变更方法。
四、推荐阅读
Object.defineProperty() - MDN
最通俗易懂的Vue3响应式核心原理解析
手写一个简易vue响应式带你了解响应式原理