观察者模式
在前端领域应用非常广泛。
概念
当对象间存在一对多关系时,则使用观察者模式。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
UML 类图
代码演示
// src/demo05/observer.ts
class Subject {
private state = 0;
private observers: Observer[] = [];
getState(): number {
return this.state;
}
setState(newState: number) {
this.state = newState;
this.notify();
}
// 添加观察者
attach(observer: Observer) {
this.observers.push(observer);
}
// 通知
private notify() {
this.observers.forEach((observer) => {
observer.update(this.state);
});
}
}
// 观察者
class Observer {
name: string;
constructor(name: string) {
this.name = name;
}
update(state: number) {
console.log(`${this.name} update, state is ${state}`);
}
}
const sub = new Subject();
const observerA = new Observer('A');
sub.attach(observerA);
const observerB = new Observer('B');
sub.attach(observerB);
sub.setState(1);
export {};
场景
DOM 事件就是最常用的观察者模式
// demo05/dom.html
<buttonid="btn1">btn</button>
<script>
const $btn1=$('#btn1')
$btn1.click(function(){
console.log(1)
})
$btn1.click(function(){
console.log(2)
})
$btn1.click(function(){
console.log(3)
})
</script>
React、Vue 的生命周期
Vue 的生命周期
Vue watch
// Vue组件配置
{
data(){
name:'leslie'
},
watch:{
name(newVal,val){
console.log(newValue,val)
}
}
}
Vue 组件更新过程
React 组件更新过程不是这样的,它是通过 setState 主动触发的,而非响应式监听。
异步回调
定时器 setTimeout、setInterval
Promise then 回调
参考:src/demo2/promise.ts
nodejs stream
// src/demo05/stream.js
const fs = require('fs');
const readStream = fs.createReadStream('./data/file.txt'); // 读取文件
let length = 0;
readStream.on('data', function (chunk) {
length += chunk.toString().length;
});
readStream.on('end', function () {
console.log(length);
});
nodejs readline
// src/demo05/readline.js
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: fs.createReadStream('./data/file.txt'),
});
let lineNum = 0;
rl.on('line', function (line) {
lineNum++;
});
rl.on('close', function () {
console.log(lineNum);
});
nodejs http server 回调
// src/demo05/http.js
const http = require('http');
function serverCallback(req, res) {
console.log('get 请求不处理', req.url);
res.end('hello');
}
http.createServer(serverCallback).listen(8081);
console.log('监听 8081 端口...');
MutationObserver
src/demo05/MutationObserver.html
<div id="container">
<p>A</p>
<p>B</p>
</div>
function callback(records: MutationRecord[], observer: MutationObserver) {
for (let record of records) {
console.log('record: ', record);
}
}
const observer = new MutationObserver(callback);
const containerElem = document.getElementById('container');
const options = {
attributes: true, // 监听属性变化
attributeOldValue: true, // 变化之后,记录旧属性的值
childList: true, // 监听子节点变化(新增删除)
characterData: true, // 监听节点内容或文本变化
characterDataOldValue: true, // 变化之后,记录旧内容
subtree: true, // 递归监听所有下级节点
};
// 开始监听
observer.observe(containerElem, options);
// 停止监听
observer.disconnect();
vs 发布订阅模式
它是观察者模式的另一个版本
event.on('event-key', () => {
// 事件1
});
event.on('event-key', () => {
// 事件2
});
// 触发执行
event.emit('event-key');
观察者模式 vs 发布/订阅模式
观察者模式
- Subject 和 Observer 直接绑定,中间无媒介
- 如 addEventListener 绑定事件
发布/订阅模式
- Publisher 和 Observer 互不交集,中间有媒介
- 如 event 自定义事件
一个很明显的特点:发布订阅模式需要在代码里触发 emit,而观察者没有。
场景
自定义事件
- Vue 2 实例本身就支持自定义时间,但 Vue3 不支持
- Vue 3 推荐使用 mitt,轻量级 200 bytes
import mitt from 'mitt';
const emitter = mitt();
// listen to an event
emitter.on('foo', (e) => console.log('foo', e));
// listen to all events
emitter.on('*', (type, e) => console.log(type, e));
// fire an event
emitter.emit('foo', { a: 'b' });
// clearing all events
emitter.all.clear();
- 但是,mitt 没有 once,可以借用 event-emitter
postMessage 通讯
通过 window.postMessage 发送消息。注意第二个参数,可以限制域名, 如发送敏感信息,要限制域名。
// 父页面向 iframe 发送消息
window.iframe1.contentWindow.postMessage('hi', '*');
// iframe 向父页面 发送消息
window.parent.postMessage('wo', '*');
// 可监听 message 来接收消息。可使用 event.origin 来判断消息来源是否合法,可选择不接受
window.addEventListener('message', (event) => {
console.log('origin', event.origin); // 通过 origin 判断是否来源合法
console.log('child received', event.data);
});
同类型的还有:
- nodejs 多进程通讯
- WebWorker 通讯
- WebSocket 通讯
注意
在 Vue 和 React 组件中使用,在组件销毁之前,要及时 off(销毁)自定义事件。否则可能会导致内存泄漏。另,off 时要传入原来的函数,而不能是匿名函数。
设计原则验证
- 主题和观察者分离,不是主动触发而是被动监听,二者解耦
- 符合开放封闭原则