1.如何追踪变化,现在稍微会写vue的都知道2.0的时候是利用Object.defineProperty
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
return val;
},
set: function reactiveSetter(newVal) {
if (newVal === val) {
return;
}
val = newVal;
}
}
)}
2.那么这样写了离响应式还是很远的,这时候我们可以这样想,如果有对应的实例使用当前对象,那么就被收集到当前的依赖中
function defineReactive(obj, key, val) {
let dep = [];
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
dep.push(this.$vm);
return val;
},
set: function reactiveSetter(newVal) {
if (newVal === val) {
return;
}
// 某种触发当前的dep
for(let i = 0;i < dep.length;i++) {
dep[i].notify(); // 假设notify是可以让对应实例重新渲染的函数
}
val = newVal;
}
}
)}
3.思考到这里我们可以发现我们只是想到了最简单的一种情况,大概是这样
export default {
data () {
return {
a: 1,
b: '1'
}
}
}
4.如果a属性也是对象怎么办,那一般大家会想到的是递归
class Observe {
constructor(value) {
this.value = value;
if (!Array.isArray(value)) {
this.walk(value);
}
}
walk(obj) {
const keys = Object.keys(obj);
for(let i = 0;i < keys.length;i++) {
defineReactive(obj, keys[i], obj[keys[i]]);
}
}
}
function defineReactive(obj, key, val) {
if (typeof obj[key] === 'object') {
new Observe(val);
}
let dep = [];
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
dep.push(this.$vm);
return val;
},
set: function reactiveSetter(newVal) {
if (newVal === val) {
return;
}
// 某种触发当前的dep
for(let i = 0;i < dep.length;i++) {
dep[i].notify();
// 假设notify是可以让对应实例重新渲染的函数
}
val = newVal;
}
}
)}
5.总结
响应式的目的是当数据发生变化时,页面可以立马渲染更新;在vue2.0中实现了通过Object.defineProperty将属性的读取和更新转换为getter和setter的形式来追踪变化,当读取数据的时候会走到getter,当改变数据的时候会走到setter,observer的作用是将一个对象的所有属性都设置为响应式的,每个属性都有自己的依赖集合dep,当一个数据被多个实例读取的时候,这多个实例都会被push到这个集合中,当数据变化的时候,setter中会循环处理dep依次通知所有的实例更新
6.备注
从上面简单的实现过程中,我们可以发现,如果属性是没有提前在data中定义的,那么是做不到响应式的,应对方式$set