从 Timeline 探索 Vue2 源码(二)——响应式改造
KevinHu-1024 opened this issue · 0 comments
KevinHu-1024 commented
未完待续,pending...
书接上回 #1
响应式要达到的几种效果
- 拦截扁平对象键值的get/set
var a = {
name: 'kevin',
age: 0,
};
for(let key in a) {
if(a.hasOwnProperty(key)) {
reactive(a, key, a[key]); // 对每一个键,生成一个闭包,把键值传入
}
}
function reactive(obj, key, val) {
Object.defineProperty(obj, key, {
get: function() {
console.log(`访问${key}`);
return val; // get的时候访问闭包中的val
},
set: function(newVal) {
console.log(`写入${key}:${newVal}`);
val = newVal; // set的时候修改闭包中的val,实际上a中的属性根本就没改,只是set被我们拦截了下来,改了get要返回的闭包中的值而已
}
});
}
// 测试
- 对于嵌套的对象,能够保证上一条效果
var a = {
name: 'kevin',
age: 0,
location: {
province: 'bj',
detail: {
district: 'haidian',
}
}
};
function walk(obj) {
const isObject = (value) => Object.prototype.toString.call(value) === '[object Object]';
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var item = obj[key];
if (isObject(item)) {
arguments.callee(item);
} else {
reactive(obj, key, item); // 对每一个不是对象的键,生成一个闭包,把键值传入
}
}
}
}
function reactive(obj, key, val) {
Object.defineProperty(obj, key, {
get: function() {
console.log(`访问${key}`);
return val; // get的时候访问闭包中的val
},
set: function(newVal) {
console.log(`写入${key}:${newVal}`);
val = newVal; // set的时候修改闭包中的val
}
});
}
walk(a);
// 测试
- 按照源码的风格封装了一下:
var a = {
name: 'kevin',
age: 0,
location: {
province: 'bj',
detail: {
district: 'haidian',
}
}
};
class Observer {
constructor(data) {
this.data = data;
if (Observer.isObject(data)) {
this.walk(data);
} else {
console.error(`${Object.prototype.toString.call(data)} 不是对象`);
}
}
static isObject(value) {
return Object.prototype.toString.call(value) === '[object Object]'
}
walk(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var item = obj[key];
if (Observer.isObject(item)) {
new Observer(item);
} else {
this.reactive(obj, key, item); // 对每一个不是对象的键,生成一个闭包,闭包不会被销毁
}
}
}
}
reactive(obj, key, val) {
Object.defineProperty(obj, key, {
get: function() {
console.log(`访问${key}`);
return val; // get的时候访问闭包中的val
},
set: function(newVal) {
console.log(`写入${key}:${newVal}`);
val = newVal; // set的时候修改闭包中的val
}
});
}
}
new Observer(a);
响应式的目标
- 改造一个对象 / 数组为响应式
- 当我修改对象键值 / 数组元素时,能够通知外界被更改的项及新值
- 当我访问对象键值 / 数组元素时,能够通知外界被更改的项及新值
以下面的对象为例:
var a = {
name: 'kevin',
age: 0,
location: {
province: 'bj',
detail: {
district: 'haidian',
}
}
};