这是我参与「第四届青训营 」笔记创作活动的第4天
一、本堂课重点内容:
单例模式,发布者订阅者,原型模式,代理模式,迭代器模式,组合模式。
二、详细知识点介绍:
-
设计模式是软件设计中常见问题的解决方案模型
-
设计模式三大分类:
-
创建型模式:关注如何创建一个对象
单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式;
-
结构型模式:关注如何灵活的将对象组装成较大的结构
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
-
行为型模式:负责对象间的高效通信和职责划分
模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
-
-
浏览器中的设计模式
-
单例模式
定义:全局唯一访问对象,保证一个类仅有一个实例,并提供一个访问它的全局访问点。
应用场景:缓存,全局状态管理等
-
发布者订阅者模式
定义:可在被订阅对象发生变化时通知订阅者
应用场景:从系统架构之间的解耦,到业务中一些实现模式,eg. 邮件订阅,上线订阅等
-
-
Javascripth中的设计模式
-
原型模式
定义:复制已有对象来创建新的对象
应用场景:JS中对象创建的基本模式
-
代理模式
定义:可自定义控制对原对象的访问方式,并且允许在更新前后做一些额外处理
应用场景:监控,代理工具,前端框架实现
-
迭代器模式
定义:在不暴露数据类型的情况下访问集合中的数据
应用场景:数据结构中有多种数据类型,列表,树等,提供通用操作接口
-
-
前端框架中的设计模式
-
代理模式
直接访问会给使用者或者系统结构带来很多麻烦(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),我们可以在访问对象时加上一个对此对象的访问层。
eg. 框架中对DOM操作的代理:
-
组合模式
将对象组合成树形结构以表示"部分-整体"的层次结构。 eg. React的组件结构,count组件既能独立渲染,又能在App中渲染,
function App() { return ( <div className="App"> <Header /> <Count /> <Footer /> </div> ); }
-
三、实践练习例子:
-
用单例模式实现请求缓存
即对于相同的情节路径,第二次发送请求时复用上一次请求的某些值。
import { api } from "./utils"; // 创建Request类 export class Request { static instance: Request; private cache: Record<string, string>; constructor() { this.cache = {}; } // 创建单例对象 static getInstance() { if(this.instance) { return this.instance; } this.instance = new Request(); return this.instance; } // 向url发送请求 public async request(url: string) { if(this.cache[url]) { //如果缓存中存在,则直接返回 return this.cache[url]; } const response = await api(url); this.cache[url] = response; return response; } } -
用发布订阅模式实现用户上线订阅
type Notify = (user: User) => void; // 创建user类 export class User { name: string; status: "offline" | "online"; // 离线/在线 followers: { user:User; notify: Notify }[]; // 订阅数组,调用函数 //构造器中设置初始状态为离线 constructor(name: string) { this.name = name; this.status = "offline"; this.followers = []; } subscribe(user:User, notify: Notify) { user.followers.push({user, notify}); } online() { // 用户上线 调用函数 this.status = "online"; this.followers.forEach( ({notify}) => { notify(this); }); } } -
用原型模式创建上线订阅中的用户
// 原型 const baseUser:User = { name: "", status: "offline", followers: [], // 与上一个设计模式比,没有构造器 subscribe(user, notify) { user.followers.push({user, notify}); }, online() { // 用户上线 调用函数 this.status = "online"; this.followers.forEach( ({notify}) => { notify(this); }); } } // 在该原型上创建对象的函数 export const createUser = (name:string) => { const user:User = Object.create(baseUser); user.name = name; user.followers = []; return user; }; -
用代理模式实现用户状态订阅
type Notify = (user: User) => void; export class User { name: string; status: "offline" | "online";// 离线/在线 followers: { user:User; notify: Notify }[]; // 订阅数组,调用函数 constructor(name: string) { this.name = name; this.status = "offline"; this.followers = []; } subscribe(user:User, notify: Notify) { user.followers.push({user, notify}); } online() { // 用户上线 调用函数 this.status = "online"; this.followers.forEach( ({notify}) => { notify(this); }); } } -
用for of 迭代所有组件
class MyDomElement { tag: string; children: MyDomElement[]; constructor(tag:string) { this.tag = tag; this.children = []; } addChildren(component: MyDomElement) { this.children.push(component); } [Symbol.iterator]() { const list = [...this.children]; //list 队列 let node; return { next: () => { while((node = list.shift())) { // 从队首取节点 node.children.length > 0 && list.push(...node.children);//如果有孩子结点将其添加到队尾 return { value: node, done: false }; } return { value:null, done:true }; }, }; } } -
Vue组件实现计数器
<template> <button @click="count++">count is:{{ count }}</button> </template> <script setup lang="ts"> import { ref, onBeforeUpdate, onUpdated } from "vue"; const count = ref(0); const dom = ref<HTMLButtonElement>(); onBeforeUpdate(() => { console.log("Dom before update", dom.value?.innerText); }); onUpdated(() => { console.log("Dom after update", dom.value?.innerText); }); </script>
四、课后个人总结:
设计模式有很多种,只是听听看看是不可能掌握的,要在实际开发过程中想办法套用设计模式,它带给我们的更多的是一种编码思路,帮助我们写出可维护性较好的代码。
设计模式是前辈们在大量开发总积累的经验,在问题面前,重要的不是是否使用了某个设计模式,而是你是否能将这些模式内化,将模式的思想转化为代码。
关于设计模式的学习还有很长一段路要走。
五、引用参考
前端设计模式应用-吴立宁老师