实现一个最小版本vue(二)之observer
observer
功能
-
负责把data选项中的属性转换成响应式数据
-
data中的某个属性也是对象,把该属性转换成响应式数据。
-
数据变化发出通知
-
结构

实现思路
walk方法判断传入的data是否是对象,加强代码健壮性- 遍历
data对象所有属性,调用defineReactive,转化为响应式数据 defineReactive需要传3个参数,第三个参数直接把val传入,而不是通过obj[key]获取,是因为,访问实例属性时,首先触发vue.js里的get方法,这个get调用了data[key],这里会触发observer里的get方法,如果在observer里的get里直接使用obj[key],则又触发了这个get方法,产生一个死递归,会发生堆栈溢出错误- 这第三个参数
val是局部对象,执行完成应该被释放,这里没有被释放,原因是:传入的obj是data对象($data),而$data引用了get方法,外部引用了这个方法,而get又用到了val,所以不会释放掉,产生了闭包 - 如果
data对象里还包含对象,需要在defineReactive开始时也调用一次walk,判断内部是否还有嵌套,把嵌套对象也转换成响应式 - 修改
data中的值,修改后的值也需要是响应式的,所以在set方法里,也调用一次walk
代码
class Observer {
constructor (data) {
this.walk(data)
}
walk (data) {
// 1.判断data是否时对象
if (!data || typeof data !== 'object') {
return
}
// 2.遍历data对象所有属性
Object.keys(data).forEach(key => {
this.defineReactive(data, key, data[key])
})
}
// 定义响应式数据
defineReactive (obj, key, val) {
// 如果val此时是对象,会把val内部的属性转换成响应式的
this.walk(val)
const that = this
let dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get () {
// 收集依赖
Dep.target && dep.addSub(Dep.target)
// 如果使用obj[key],则又触发了get方法,死递归,会发生堆栈溢出错误
// return obj[key]
return val
},
set (newValue) {
if (newValue === val) {
return
}
val = newValue
// 新值也要通过walk进行响应式转换
that.walk(newValue)
// 发送通知
dep.notify()
},
})
}
}

浙公网安备 33010602011771号