前端设计模式与应用 | 青训营笔记

148 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第6天

一、什么是设计模式

1.定义:

软件设计中常见问题的解决方案模型

  • 历史经验的总结
  • 与特定语言无关

2.背景:

“设计模式”一词最早出现在1977年出版的一本建筑领域的书中

3.设计模式分类

《设计模式:可复用面向对象软件的基础》中提到23种设计模式并划分了三个类型:

  • 创建型——如何创建一个对象
  • 结构型——如何灵活的将对象组装成较大的结构
  • 行为型——负责对象间的高效通信和职责划分

二、前端场景下的设计模式

1.浏览器(api)中的设计模式

  • 单列模式
  • 发布订阅模式

⑴单列模式

定义:全局唯一访问对象(一个类仅有一个实例)。

简单理解:在任何地方访问的都是同一个对象,如果在任意一个地方对该对象进行修改,其它用到该对象的地方也会同步这次修改的结果。

例:

浏览器当中的window对象——对浏览器当中所有的操作进行了封装。

应用场景

  • 缓存
  • 全局状态管理
用单例模式实现请求缓存

使用场景:在不同的时间/地方发送相同的url请求,希望在第二次发送请求时可以复用之前的值(缓存),以便更好的用户体验

实现代码

typescript语法实现:

import {api} from "./utils"   //定义一个mock的api,每500ms返回一个值
export class Requset {
  static instance: Requset;//用来存储全局唯一实例
  private cache: Record<string, string>//用来存储缓存的值
  constructor() {
    this.cache = {};//初始化缓存内容
  }
  //下面这段代码运用了单例模式
  static getInstance() {
    //仅在第一次创建了一个新的实例,后面都会用到这个实例
    if (this.instance) {return this.instance;}
    this.instance = new Requset();
    return this.instance;
  }
  //缓存实现
  public async request(url: string){
    if (this.cache[url]) {
      return this.cache[url];
    }
    const response = await api(url);
    this.cache[url] = response;
    return response;
  }
}

使用:


用JavaScript语法实现:

import {api} from "./utils";
const cache:Record<string,string>={};
export const request = async(url:string)=>{
  if(cache[url]){
    return cache[url];
  }
  const response = await api(url);
  cache[url] = response;
  return response;
}

使用:

⑵发布订阅模式(观察者模式)

定义:一种订阅机制(消息范式),可在被订阅对象发生变化时通知订阅者。

应用场景:从系统架构之间的解耦,到业务中一些实现模式,像邮件订阅,上线订阅等等,应用广泛。

例:

用发布订阅模式实现用户上线订阅

实现代码:

type Notify = (user: User) =>void;
export class User {
  name: string;//用户名
  status:"offline" | "online ";//用户状态
  //followers代表订阅的人
  followers: { user: User; notify : Notify }[];
  //初始化
  constructor(name: string) {
    this.name = name;
    this.status = "offline" ;
    this.followers = [];
  }
  //订阅方法
  //第一个参数:需要订阅的人
  //第二个参数:订阅的人上线的通知函数
  subscribe(user: User, notify: Notify) {
    user.followers. push(i user, notify });
}
  online() {
    //上线
    this.status = "online" ;
    //通知所有订阅自己的人
    this.followers.forEach(({ notify }) =>{
      notify(this);
    });
  }
}

使用:

2.JavaScript中的设计模式

  • 原型模式
  • 代理模式
  • 迭代器模式

⑴原型模式

定义:复制已有对象来创建新的对象

应用场景:JavaScript对象创建的基本模式

例:

原型链中的原型:每个构造函数都会有一个prototype属性,prototype就是调用构造函数所创建的那个实例对象的原型,所有对象实例都可以共享prototype身上的属性和方法。

用原型模式创建上线订阅中的用户

实现代码:

//作为原型的对象
const baseUser: User = {
    name: "",
    status: "offline " ,
    followers: [],
    subscribe(user, notify) {
        user.followers.push(i user, notify });
    },
    online(){
        this.status = "online" ;
        this.followers.forEach(({ notify })=>{
            notify(this);
        });
    }
}
//基于原型创建新的对象
export const createUser = (name: string) =>{
    const user: User = 0bject.create(baseUser);
    user.name = name;
    user.followers = [];
    return user;
};

使用:

⑵代理模式

定义:可自定义控制原对象的访问方式,并且允许在更新前后作一些额外处理。

应用场景:监控、代理工具、前端框架实现等。

使用代理模式实现用户状态订阅

实现代码:

export const createProxyUser = (name: string) =>{
  const user = new User (name);
  const proxyUser = new Proxy(user,{
    set: (target, prop: keyof User, value) =>{
      target[prop] = value;
      if (prop ≡ "status ") {
        notifyStatusHandlers(target, value);
      }
      return true;
    }
  });
  const notifyStatusHandlers = (user: User,status: "online" | "offline") =>{
    if (status ≡ "online") {
      user.followers.forEach((i notify })=>{
        notify(user);
      });
    }
  }
  return proxyUser;
};

⑶迭代器模式

定义:在不暴露数据类型的情况下访问集合中的数据

应用场景:数据结构中有多种数据类型,列表,树等,提供通用操作接口

3.前端框架中的设计模式

  • 代理模式
  • 组合模式

⑴代理模式:

定义: 为一个对象提供一个代用品或占位符,以便控制对它的访问。

应用场景:

监听变量的变化:前端框架、状态管理库

代理对象的访问方式(拦截器):例如axiosinterceptor

⑵组合模式

定义: 可多个对象组合使用,也可对单个对象独立使用

应用场景: DOM、前端组件、****文件目录、部门

例: React的组件结构

function App(){
  return (
    <div className="App">
      <Header/>
      <Count/>
      <Footer/>
    </div>
  );
}

总结

设计模式不是银弹

  • 总结出抽象的模式相对比较简单,但是想要将抽象的模式套用到场景中却非常困难
  • 现代编程语言的多编程范式带来的更多可能性
  • 真正优秀的开源项目学习设计模式并不断实践