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

102 阅读5分钟

什么是设计模式?

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

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

设计模式的趋势

屏幕截图 2023-08-11 103342.png

23种设计模式的分类:

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

浏览器中的设计模式

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

单例模式

定义: 全局唯一访问对象

单例模式是一种对象创建模式,它用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例

确保一个类只有一个实例,并提供一个全局访问点。它通常用于管理共享资源或全局状态。

单例模式的主要目的是限制一个类的实例化次数,以及确保在整个应用程序中只能有一个相同的对象实例被使用。这在某些情况下是非常有用的,例如需要一个全局共享的对象,或者需要控制资源的访问次数。

实现单例模式的关键是将类的构造函数设置为私有,防止外部通过调用构造函数来创建实例。然后通过一个静态方法来获取实例,并在第一次调用该方法时创建实例。随后的调用都会返回之前创建的实例。

通俗理解就是一个类的对象只能拥有一个实例,没有则创建该对象的实例化对象,有则不会重新创建而是返回该实例化后的对象

应用场景: 缓存,全局状态管理

用单例模式实现请求缓存

import { api } from"./utils" ;      //定义api,500m后返回

export cLass Requset {//定义请求实例
    static instance: Requset;//定义缓存
    private cache: Recordestring, 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[urU]){ 
            return this . cache[urL];
        }
        const response = await api(urL); 
        this . cache[urL] = response;
        
        return response;
    }
}
test("should response more than 500ms with class", async () = {
    const request = Requset. getInstance();

    const startTime = Date . now();
    await request . request(" /user/1");
    const endTime = Date. now();

    const costTime = endTime - startTime;
    expect ( costTime) . toBeGreaterThanOrEquaL(500);
});


test("should response quickly second time with class", async () = {
    const request1 = Requset . getInstance();
    await request1 . request(" /user/1");

    const startTime = Date now();
    const request2 = Requset . getInstance();
    await request2 . request(" /user/1");
    const endTime = Date . now();

    const costTime = endTime - startTime;
    expect ( costTime) . toBeLessThan(50);
});

以上是一个传统的单例模式的情景,是一个传统的面向对象的内容

那么如何用JavaScript不用Class来实现单例?

    import { api } from "./utils";

    const cache: Record<string, string> = {};
    
    export const request = async (urT: string) →{
        if (cache[urL]) {
            return cache[urL];
        }
    
    const response = await api(urL);
    
    cache[urL] = response;
    return response;
};

发布订阅模式

定义: 【又称观察者模式】

定义了对象之间的一对多依赖关系,当一个对象状态变化时,它的所有依赖对象都会收到通知并自动更新。广播通信模式(Pub/Sub)是观察者模式的一种实现。

  • 一种订阅机制,可在被订阅对象发生变化时通知订阅者

应用场景:

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

在观察者模式中,存在两种关键角色:

  1. 主题(Subject):也称为可观察对象(Observable),主题维护一组观察者,并提供方法来添加、移除和通知观察者。主题的状态变化会导致观察者的更新。
  2. 观察者(Observer):观察者订阅主题,以接收主题的通知。当主题的状态发生变化时,观察者会被通知,并根据需要执行相应的操作。

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

type Notify = (user: User) => void;

export class User {
  name: string;
  status: "offline" | "online";
  // user 订阅自己的人,notify 上线时的通知函数
  followers: { user: User; notify: Notify }[];

  constructor(name: string) {
    this.name = name;
    this.status = "offline";
    this.followers = [];
  }

  // 订阅参数中的 user
  subscribe(user: User, notify: Notify) {
    user.followers.push({ user, notify });
  }

  // 状态online——上线
  online() {
    // 状态改为 online
    this.status = "online";
    // 通知所有订阅自己的人
    this.followers.forEach(({ notify }) => {
      notify(this);
    });
  }
}
test("should notify followers when user is online for multiple users", () => {
  // 创建三个用户
  const user1 = new User("user1");
  const user2 = new User("user2");
  const user3 = new User("user3");

  // 通知 user1 和 user2 的函数
  const mockNotifyUser1 = jest.fn();
  const mockNotifyUser2 = jest.fn();

  // user1 订阅了 user3 的上线,传入通知 user1 的函数
  user1.subscribe(user3, mockNotifyUser1);
  user2.subscribe(user3, mockNotifyUser2);

  // user3 上线
  user3.online();

  // user3 会调用通知 user1 的函数
  expect(mockNotifyUser1).toBeCalledWith(user3);
  expect(mockNotifyUser2).toBeCalledWith(user3);
});

JavaScript中的设计模式

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

原型模式

JavaScript中有一个特性叫原型链,用来实现继承等

定义:

复制已有对象来创建新的对象

应用场景:

JS中对象创建的基本模式

代理模式

定义:

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

应用场景:

监控,代理工具,前端框架实现等等

迭代器模式

定义:

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

应用场景:

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

用for of 迭代所有组件

// 浏览器中的 DOM 结构
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];
    let node;

    return {
      // for...of 迭代时调用的函数
      next: () => {
        while ((node = list.shift())) {
          // 层序遍历
          node.children.length > 0 && list.push(...node.children);
          // value 是迭代出的值,done 是指迭代是否完成
          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 } from "vue ";
const count = ref(0);
</script>

前端框架中对·DOM操作的代理

屏幕截图 2023-08-11 164347.png

组合模式

定义:

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

应用场景:

DOM,前端组件,文件目录,部门