稍微学一下Vue的数据响应式(Vue2及Vue3区别)

2020-06-12 20:51:33易采站长站整理

什么是数据响应式

从一开始使用 Vue 时,对于之前的 jq 开发而言,一个很大的区别就是基本不用手动操作 dom,data 中声明的数据状态改变后会自动重新渲染相关的 dom。
换句话说就是 Vue 自己知道哪个数据状态发生了变化及哪里有用到这个数据需要随之修改。

因此实现数据响应式有两个重点问题:

如何知道数据发生了变化?
如何知道数据变化后哪里需要修改?

对于第一个问题,如何知道数据发生了变化,Vue3 之前使用了 ES5 的一个 API Object.defineProperty Vue3 中使用了 ES6 的 Proxy,都是对需要侦测的数据进行 变化侦测 ,添加 getter 和 setter ,这样就可以知道数据何时被读取和修改。

第二个问题,如何知道数据变化后哪里需要修改,Vue 对于每个数据都收集了与之相关的 依赖 ,这里的依赖其实就是一个对象,保存有该数据的旧值及数据变化后需要执行的函数。每个响应式的数据变化时会遍历通知其对应的每个依赖,依赖收到通知后会判断一下新旧值有没有发生变化,如果变化则执行回调函数响应数据变化(比如修改 dom)。

下面详细分别介绍 Vue2 及 Vue3 的数据变化侦测及依赖收集。

Vue2

变化侦测

Object 的变化侦测

转化响应式数据需要将 Vue 实例上 data 属性中定义的数据通过递归将所有属性都转化为 getter/setter 的形式,Vue 中定义了一个 Observer 类来做这个事情。


function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}

class Observer {
constructor(value) {
this.value = value;
def(value, '__ob__', this);
if (!Array.isArray(value)) {
this.walk(value);
}
}
walk(obj) {
for (const [key, value] of Object.entries(obj)) {
defineReactive(obj, key, value);
}
}
}

直接将一个对象传入 new Observer() 后就对每项属性都调用 defineReactive 函数添加变化侦测,下面定义这个函数:


function defineReactive(data, key, val) {
let childOb = observe(val);
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function () {
// 读取 data[key] 时触发
console.log('getter', val);
return val;
},
set: function (newVal) {
// 修改 data[key] 时触发
console.log('setter', newVal);
if (val === newVal) {
return;
}
val = newVal;
}
})
}

function observe(value, asRootData) {
if (typeof val !== 'object') {