一、观察者模式
- 观察者(订阅者) -- Watcher
update():当事件发生时,具体要做的事情
- 目标(发布者) -- Dep
subs 数组:存储所有的观察者
addSub():添加观察者
notify():当事件发生,调用所有观察者的 update() 方法
- 没有事件中心
// 目标(发布者)
// Dependency
class Dep {
constructor() {
// 存储所有的观察者
this.subs = []
}
// 添加观察者
addSub(sub) {
if (sub && sub.update) {
this.subs.push(sub)
}
}
// 通知所有观察者
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
}
// 观察者(订阅者)
class Watcher {
update() {
console.log('update')
}
}
// 测试
let dep = new Dep()
let watcher = new Watcher()
dep.addSub(watcher)
dep.notify()
- 总结
观察者模式是由具体目标调度,比如当事件触发,Dep 就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间是存在依赖的。
发布/订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在。
二、响应式原理
- watcher在第一次视图绑定变量的时候(插值表达式、指令处理时)创建,每个key有自己的一个watcher。
- 创建watcher的时候应该加到对应的发布者里去,所以在watcher构造函数处调用
dep.addSub(watcher)。
- 发布者当数据变更时需要通知观察者
dep.notify(),所以发布者的实例创建即new Dep()应该是在数据变更处,即Observer的walk里。
- 而watcher构造函数里为了能调用到Observer的walk里defineProperty的dep实例,可以借用
this.oldValue = vm[key]去操作。所以可以把dep.addSub(watcher)写在getter里,并通过Dep.target控制只有第一次才addSub。
// 处理插值表达式
compileText(node) {
const regExp = /\{\{(.+?)\}\}/
const value = node.textContent
if (regExp.test(value)) {
const key = RegExp.$1.trim()
node.textContent = value.replace(regExp, this.vm[key])
// 为当前的key创建watcher,当key变化时更新视图
new Watcher(this.vm, key, (newValue) => {
node.textContent = newValue
})
}
}
class Watcher {
// 每个数据变化都要触发更新,所以每个key对应一个watcher
constructor(vm, key, cb) {
this.vm = vm
this.key = key
this.cb = cb
// 创建watcher的时候应该加到对应的发布者里去,发布者当数据变更时需要通知观察者,所以发布者应该是在数据变更处,那么添加watcher也可以放在对应地方即Observer的walk里
// 此处跟Observer里walk相关的也就获取旧数据,为了在walk里面触发添加watcher,可以把添加写在getter里,并通过Dep.target控制只有第一次才addSub
// 把watcher对象记录到Dep类的静态属性target
Dep.target = this
// 触发get方法,在get方法中会调用addSub
this.oldValue = vm[key]
Dep.target = null
}
// 当数据变化的时候更新视图
update() {
let newValue = this.vm[this.key]
if (this.oldValue === newValue) {
return
}
this.cb(newValue)
}
}
class Observer {
constructor(data) {
this.walk(data)
}
walk(data) {
if (!data || typeof data !== 'object') {
return
}
Object.keys(data).forEach(key => {
const that = this
const val = data[key] // 避免循环先存下来
const dep = new Dep() // 负责收集依赖,当数据变化的时候通知观察者
this.walk(val)
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get() {
// 只有创建watcher时Dep.target才存在,所以只有创建watcher的时候获取数据才会触发添加watcher
Dep.target && dep.addSub(Dep.target)
return val
},
set(newVal) {
if (newVal === val) {
return
}
that.walk(newVal)
val = newVal // val被闭包缓存了,此处修改的是当前作用域下的val,get返回的也是同一个val
dep.notify() // 通知观察者调用update
}
})
})
}
}
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。