请结合上一期观看
1.创建Obsever,给属性添加defineProperty,
// 给每个属性添加响应式
class Obsever {
constructor(vm) {
this.vm = vm
this.data = vm.$data
this.obsever()
}
obsever () {
const dep = new Dep()
for (let key in this.data) {
let val = this.data[key]
Object.defineProperty(this.data, key, {
get () {
Dep.target && dep.addSub(Dep.target)
return val
},
set (newVal) {
val = newVal
//触发相应的更新回调
dep.notify()
}
})
}
}
}
2.创建Watcher类
作用:在之后数据变化时,不直接去通知依赖更新,而是通知依赖对应的Watch实例,由Watcher实例去通知真正的视图,可以理解成watch类就代表这个依赖。
PS:谁用到了数据,谁就是依赖,我们就为谁创建一个Watcher实例,在创建Watcher实例的过程中会自动的把自己添加到这个数据对应的依赖管理器中,以后这个Watcher实例就代表这个依赖,当数据变化时,我们就通知Watcher实例,由Watcher实例再去通知真正的依赖。
当数据变化时,会触发数据的setter,在setter中调用了dep.notify()方法,在dep.notify()方法中,遍历所有依赖(即watcher实例),执行依赖的update()方法,也就是Watcher类中的update()实例方法,在update()方法中调用数据变化的更新回调函数,从而更新视图。
class Watcher {
constructor(vm, key, cb) {
this.vm = vm
this.cb = cb
this.oldVaL = this.getOldVal(key, vm)
}
getOldVal(key, vm) {
Dep.target = this
const oldVal = vm.$data[key]
Dep.target = null
return oldVal
}
update() {
this.cb()
}
}
3.创建Dep类(依赖管理器)
在getter中收集依赖,在setter中通知依赖更新。
class Dep {
constructor() {
this.subList = []
}
//添加监听对象
addSub (watcher) {
this.subList.push(watcher)
}
//执行对象响应回调
notify () {
this.subList.forEach((sub) => {
sub.update()
})
}
}
4.在我上期作品的Compile类里添加以下代码
//文本节点
compileText (node) {
const con = node.textContent
// 解析出{{}},变量赋值
const reg = /\{\{(.+?)\}\}/g
if (reg.test(con)) {
const newVal = con.replace(reg, (...arg) => {
// arg[1]就是data中的变量名,此处为msg、info
new Watcher(this.vm, arg[1], () => {
//更新页面,会在defineProperty的set方法调用此回调
const xx = con.replace(reg, (...arg) => {
return this.vm.$data[arg[1]]
})
node.textContent = xx
})
return this.vm.$data[arg[1]]
})
node.textContent = newVal
}
}
5.点击就可以实现实时更新
处理v-model
//元素节点
compileElement (node) {
//获取标签的属性
const attrs = node.attributes
//如果属性长度>0
if (!!attrs.length) {
//将attrs类数组转化为真正的数组
const attrsArr = Array.from(attrs)
attrsArr.forEach((att) => {
// 如果有v-model属性就讲属性的值赋值给data里面的值
if (att.nodeName === 'v-model') {
const val = att.value
node.value = this.vm.$data[val]
if (node.tagName.toLowerCase() === 'input') {
// 在这里处理input事件
node.addEventListener('input', (event) => {
this.vm.$data[val] = event.target.value
});
}
}
})
}
}
效果图
object侦测总结:
1.Data通过observer转换成了getter/setter的形式来追踪变化。
当外界通过Watcher读取数据时,会触发getter从而将Watcher添加到依赖(Dep)中。
2.当数据发生了变化时,会触发setter,从而向Dep中的依赖(即Watcher)发送通知。
3.Watcher接收到通知后,会向外界发送通知,变化通知到外界后可能会触发视图更新,也有可能触发用户的某个回调函数等。
如有不足,还请多多指教!