文章目录
我们在使用Vue时,只需要修改数据,视图就会自动更新,这就是数据响应
今天来学习Vue实现数据响应式的原理~
源码链接 https://gitee.com/ykang2020/vue_learn
0. 使用Vue响应式时的学习笔记
- Vue会监视data中所有层次的数据
- 如何监测对象中的数据?
通过setter实现监视,且要在new Vue 时就传入要监测的数据
① 对象中后追加的属性,Vue默认不做响应式处理
② 如需给后添加的属性做响应式,需要使用如下API
Vue.set(target, propertyName/index, value)
vm.$set(target, propertyName/index, value)
- 如何检测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事
① 调用原生数组对应的方法对数组进行更新
② 重新解析模板,进而更新页面 - 在Vue修改数组中的某个元素一定要用如下方法
① API: push() pop() shift() unshift() splice() sort() reverse()
② Vue.set() 或 vm.$set
注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象添加属性
在学习Vue源码之前,先来看看
前置知识 Object.defineProperty()
可以参考之前的博文
【JS】JavaScript对象属性-属性类型-数据属性-访问器属性-Object.defineProperty()方法-get方法-set方法
用getter
和setter
方法可以对数据进行监听,访问和设置都会被监听捕获
读取数据的时候会触发getter,而修改数据的时候会触发setter
1. 定义 defineReactive 函数
1.1 Why (临时变量)
我们要进行数据劫持,先想到的就是Object.defineProperty()
中给属性添加getter
和setter
方法,但是这么做有点问题~
defineProperty()
方法需要临时的全局变量周转getter
和setter
我们来看下面这个例子
let obj = {
};
let temp;
Object.defineProperty(obj, "a", {
get() {
console.log("getter试图访问obj的a属性");
return temp;
},
set(newValue) {
console.log("setter试图改变obj的a属性", newValue);
temp = newValue;
},
});
console.log(obj.a);
obj.a = 5
console.log(obj.a);
1.2 How (闭包)
所以我们就自己定义一个函数,对defineProperty
进行封装,来实现数据劫持
使用defineReactive
函数不需要设置临时变量了,而是用闭包
function defineReactive(data, key, value) {
Object.defineProperty(data, key, {
// 可枚举 可以for-in
enumerable: true,
// 可被配置,比如可以被delete
configurable: true,
// getter
get() {
console.log(`getter试图访问obj的${
key}属性`);
return value;
},
// setter
set(newValue) {
console.log(`setter试图改变obj的${
key}属性`, newValue);
if (value === newValue) return;
value = newValue;
},
});
}
let obj = {
};
// 初始化
defineReactive(obj, "a", 10);
console.log(obj.a);
obj.a = 5;
console.log(obj.a);
2. 对象的响应式处理——递归侦测对象全部属性 object
2.1 Why (嵌套)
上面定义的defineProperty()
函数,不能监听到对象嵌套的形式
也就是对象嵌套对象
function defineReactive(data, key, value) {
if (arguments.length === 2) {
value = data[key];
}
Object.defineProperty(data, key, {
// 可枚举 可以for-in
enumerable: true,
// 可被配置,比如可以被delete
configurable: true,
// getter
get() {
console.log(`getter试图访问obj的${
key}属性`);
return value;
},
// setter
set(newValue) {
console.log(`setter试图改变obj的${
key}属性`, newValue);
if (value === newValue) return;
value = newValue;
},
});
}
let obj = {
b: {
c: {
d: 4,
},
},
};
// 初始化
defineReactive(obj, "b");
console.log(obj.b.c.d);
这里显示没有监听到内部(obj.b.c.d)
2.2 How(递归)
所以我们要创建一个Observer
类 ——> 将一个正常的object转换为每个层级的属性都是响应式(可以被侦测)的object
遍历对象
observe.js
监听 value
尝试创建Observer实例,如果value已经是响应式数据,就不需要再创建Observer实例,直接返回已经创建的Observer实例即可,避免重复侦测value变化的问题
import Observer from "./Observer";
/**
* 监听 value
* @param {*} value
* @returns
*/
export default function observe(value) {
// 如果value不是对象,就什么都不做
if (typeof value != "object") return;
let ob;
if (typeof value.__ob__ !== "undefined") {
ob = value.__ob__;
} else {
ob = new</