Vue 响应式——模拟封装Vue
Dep
Dep这个类的作用,是收集依赖,发送通知。
也就是在data的getter中 添加观察者,以及通知watcher
该类有三个成员,一个属性和两个方法:
- subs 观察者数组
- addSub 添加观察者方法
- notify 发送通知
class Dep {
constructor() {
this.subs = []
}
// 添加观察者
addSub(sub) {
if (sub && sub.update) {
this.subs.push(sub)
}
}
// 发送通知
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
}
watcher
功能:
- 当数据变化触发依赖, dep 通知所有的 Watcher 实例更新视图
- 自身实例化的时候往dep对象中添加自己
class Watcher {
constructor (vm,key,cb){
this.vm = vm
this.key = key
this.cb = cb
// 添加Dep静态属性target,值就是当前watcher实例
Dep.target = this
// 触发get
this.oldValue = vm[key]
Dep.target = null
}
update(){
let newValue = this.vm[this.key]
if( this.oldValue === newValue){
return
}
this.cb(newValue)
}
}
Watcher 的调用
watcher的作用是用于更新视图,所以我们可以在compiler 中的 更新视图的操作中创建 watcher 对象。
比如 在处理插值表达式的函数中
// 用于编译文本节点
compileText(node) {
// 判断文件节点中的内容是不是插值表达式
let reg = /\{\{(.+?)\}\}/
let value = node.textContent
if (reg.test(value)) {
// 获取括号分组内的字符
let key = RegExp.$1.trim()
// 替换掉插值表达式的内容
node.textContent = value.replace(reg, this.vm[key])
new Watcher(this.vm,key,(newValue)=>{
console.log(newValue);
node.textContent = newValue
})
}
}
改造 compiler 的update方法,给指令更新方法中添加 watcher
// 定义update函数来执行不同指令的更新操作
update(node, key, attrName) {
let updateFn = this[attrName + 'Updater']
updateFn && updateFn.call(this, node, this.vm[key], key)
}
// 处理 v-text 指令
textUpdater(node, value,key) {
node.textContent = value
new Watcher(this.vm, key, (newValue) => {
node.textContent = newValue
})
}
// v-model
modelUpdater(node, value, key) {
node.value = value
new Watcher(this.vm, key, (newValue) => {
node.value = newValue
})
}
实现v-model的双向数据绑定
// v-model
modelUpdater(node, value, key) {
node.value = value
new Watcher(this.vm, key, (newValue) => {
node.value = newValue
})
// 实现视图更新,更新数据
node.addEventListener('input', () => {
this.vm[key] = node.value
})
}
到此为止,简易的vue响应式原理封装完成。