mini-vue代码已放到github上。
Observer功能
- 负责把data选项中的属性转化成响应式数据
- data中的某个属性如果是对象,把该属性转换成响应式数据
- 数据变化发送通知
同样我们这节实现前两个功能。
下面通过代码来实现Observer类
Observer实现
class Observer {
constructor(data) {
this.walk(data);
}
walk(data) {
// 判断data是否为空,且是否是对象
if(!data || typeof data !== 'object') {
return
}
Object.keys(data).forEach(key => {
// 为data对象中的属性转化成响应式数据
this.defineReaction(data, key, data[key]);
})
}
defineReaction(obj, key, value) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
return value
},
set() {
if (newValue === value) {
return;
}
value = newValue;
}
})
}
}
//vue.js
class Vue {
constructor() {
...
new observer(this.$data)
}
}
我们在上述代码中的 get 和 set 直接使用了value,是为什么呢。我们可以将value换成data[key],可以看下控制台输出。
可以看到报错栈溢出,原因就是在获取元素属性的时候,每次都会掉get方法,如果value改成obj[key]的话相当于一值再走get方法,所以形成了死循环。
将obj[key]改回value我们测试下,此时控制台输出
可以看到现在data下的属性已经存在get和set方法。
此时我们在data下添加一个person属性,现在data对象为
data: {
msg: 'hello',
count: 1,
perser: {
name: 'lzx'
}
}
控制台输出为
可以看到现在person是有get和set方法的,但是person下属性name是没有get和set方法的,为了解决这一问题,其实很简单,就是在调用defineReaction方法的时候再调用一下walk方法,实现递归的处理。
defineReaction(obj, key, value) {
this.walk(value)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
return value
},
set() {
if (newValue === value) {
return;
}
value = newValue;
}
})
}
再测试下,可以看到问题已经解决
现在我们将vm的msg属性赋值成一个对象来看看结果
vm.msg = {text: 'hello'}
打开控制台来看下输出
可以看到现在msg对象下的属性并没有添加get和set方法,这个问题怎么解决呢?同样我们只需要在set值的时候,重新调用下walk方法即可。
defineReaction(obj, key, value) {
let that = this;
this.walk(value)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
return value
},
set() {
if (newValue === value) {
return;
}
value = newValue;
that.walk(newValue)
}
})
}
这块需要注意下this指向问题,然后我们打开控制台看下输出
现在可以看到问题解决,我们observer类的基本功能已经实现,我们会在后面实现数据变化发送通知的功能。