实现一个最小版本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()
},
})
}
}