likes
comments
collection
share

前端设计模式之观察者模式(五)

作者站长头像
站长
· 阅读数 62
  • 观察者模式是前端最常用的一个设计模式,也是 UI 编程最重要的思想
  • 例如在星巴克叫了杯咖啡,制作好了服务员会自动通知领取的
  • DOM 事件就是最常用的也是观察者模式
<button id="btn1">btn</button>

<script>
  const $btn1 = document.querySelector("#btn1");
  
  $btn1?.addEventListener("click", () => {});
  $btn1?.addEventListener("click", () => {});
</script>

前端设计模式之观察者模式(五)

实现主题和观察者

// 主题
class Subject {
  private state: number = 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() {
    for (const observer of this.observers) {
      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 observer1 = new Observer("A");
sub.attach(observer1);
const observer2 = new Observer("B");
sub.attach(observer2);

sub.setState(1); // 更新状态,触发观察者 update

是否符合设计原则

  • Observer 和 Target 分离,解耦

  • Observer 可自由扩展

  • Target 可自由扩展

场景

Vue React 组件生命周期

前端设计模式之观察者模式(五)

Vue watch API

// Vue 组件配置
{
  data() {
      name: "yunmu"
  },
  watch: {
      name(newVal, val) {
          console.log(newValue, val);
      }
  }
}

自定义事件

  • Vue2本身支持,Vue3不支持,Vue3 推荐使用 mitt(没有 once 绑定)文档:github.com/developit/m…
  • 注意组件销毁之前,要及时 off 自定义事件,否则可能会导致内存泄漏
  • off 时要传入原来的函数,而不能是匿名函数
import mitt from "mitt";

const emitter = mitt(); // 工厂函数

emitter.on("change", () => {
  console.log("change1");
});
emitter.on("change", () => {
  console.log("change2");
});

emitter.emit("change");

如果需要 once 绑定,可以使用 www.npmjs.com/package/eve…

import eventEmitter from "event-emitter"; // 还要安装 @types/event-emitter

const emitter = eventEmitter();

emitter.on("change", (value: string) => {
  console.log("change1", value);
});
emitter.on("change", (value: string) => {
  console.log("change2", value);
});
emitter.once("change", (value: string) => {
  console.log("change3", value);
});

emitter.emit("change", "张三");
emitter.emit("change", "李四");

各种异步的回调

  • 定时器 setTimeout setInterval Promise then nodejs stream nodejs readline nodejs http server 回调
// nodejs stream
const fs = require("fs");
const readStream = fs.createReadStream("./data/file1.txt"); // 读取文件的 stream

let length = 0;
readStream.on("data", function (chunk) {
  length += chunk.toString().length;
});
readStream.on("end", function () {
  console.log(length);
});

// nodejs readline
const readline = require("readline");
const fs = require("fs");

const rl = readline.createInterface({
  input: fs.createReadStream("./data/file1.txt"),
});

let lineNum = 0;
rl.on("line", function (line) {
  lineNum++;
});
rl.on("close", function () {
  console.log("lineNum", lineNum);
});

// nodejs http server 回调
const http = require("http");

function serverCallback(req, res) {
  console.log("get 请求不处理", req.url);
  res.end("hello");
}
http.createServer(serverCallback).listen(8081);
console.log("监听 8081 端口……");
  • Vue 组件更新过程

前端设计模式之观察者模式(五)

MutationObserver

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()

postMessage 通讯

  • 通过 window.postMessage 发送消息。注意第二个参数,可以限制域名,如发送敏感信息,要限制域名
// 父页面向 iframe 发送消息
window.iframe1.contentWindow.postMessage("hello", "*");

// iframe 向父页面发送消息
window.parent.postMessage("world", "*");

可监听 message 来接收消息。可使用 event.origin 来判断信息来源是否合法,可选择不接受

window.addEventListener("message", event => {
  console.log("origin", event.origin); // 通过 origin 判断是否来源合法
  console.log("child received", event.data);
});

同类型的还有

  • nodejs 多进程通讯

  • WebWorker 通讯

  • WebSocket 通讯

观察者模式 VS 发布订阅模式

前端设计模式之观察者模式(五)

  • 发布订阅模式可以相当于观察者模式的另一个版本
  • 观察者:SubjectObserver 直接绑定,中间无媒介,如 addEventListener 绑定事件
  • 发布订阅:PublisherObserver 相互不认识,中间有媒介,如 event 自定义事件

发布订阅模式会主动 emit,而观察者模式则没有体现

// 绑定
event.on("event-key", () => {
  // 事件1
});
event.on("event-key", () => {
  // 事件2
});

// 触发执行
event.emit("event-key");
转载自:https://juejin.cn/post/7197424371991527479
评论
请登录