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

91 阅读4分钟

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

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

什么是设计模式

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

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

设计模式背景

1.1977年《模式语言:城镇、建筑、建造》 2.1994年《设计模式:可复用面向对象软件的基础》 js并不是传统意义上面向对象的语言,因此我们更多的是学习其中的思想

设计模式分类

23种设计模式 创建型——如何更“好”的创建一个对象 结构型——如何灵活的将对象组装成较大的结构 行为型——负责对象间的高效通信和职责划分

浏览器中的设计模式

单例模式

创建一个全局唯一访问对象,常应用于缓存,全局管理状态等,例子:window

import api from "./utils"; 
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;
    }
}
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)
});

但实际上我么可以用js更简单的地实现这样的效果

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;
};
test("should response quickly second time",a
async)→
await request("/user/1");
const startTime Date.now();
await request("/user/1");
const endTime Date.now();
const costTime endTime startTime;
expect(costTime).toBeLessThan(50);
});

我们可以看到我们通过import同一个request就可以同样实现单例模式的效果,因为就是与其他传统面向对象语言不同,他们不能export出一个独立的已经初始化完成的对象或者方法而只能是一个类

发布定位模式(观察者模式)

一种订阅机制,可在被订阅对象发生变化时通知订阅者。缠影用于系统架构之间的解耦,到业务中一些实现模式,想邮件订阅,上线订阅等等,前端中的事件监听就是一个典型的发布定位模式

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

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);
});
}
}
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");
const mockNotifyUser1 = jest.fn();
const mockNotifyUser2 = jest.fn();
user1.subscribe(user3,mockNotifyUser1);
user2.subscribe(user3,mockNotifyUser2);
user3.online();
expect(mockNotifyUser1).toBeCalledWith(user3);
expect(mockNotifyUser2).toBeCalledWith(user3);
})

JavaScript中的设计模式

原型模式

复制已有对象来创建新的对象,js中对象创建的基本模式

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

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;
};
test("should notify followers when user is online for user prototypes",()=>{
const user1 = createUser("user1");
const user2 = createUser("user2");
const user3 = createUser("user3");
const mockNotifyUser1 = jest.fn();
const mockNotifyUser2 = jest.fn();
user1.subscribe(user3,mockNotifyUser1);
user2.subscribe(user3,mockNotifyUser2);
user3.online();
expect(mockNotifyUser1).toBeCalledWith(user3);
expect(mockNotifyUser2).toBeCalledWith(user3);
);

代理模式

可自定义控制对原对象的访问方式,并且允许在更新前后做一些额外的处理,常应用于前端监控,代理工具,前端框架实现等等

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

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);
});
}
}
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(({notify })=>{
notify(user);
});
}
};
return proxyUser;
};

迭代器模式

在不暴露数据类型的情况下访问集合中的数据,数据结构中有多种数据类型如,列表、树等,提供通用的操作接口

前端框架中的设计模式

代理模式

Vue的响应式原理,不必多说,相比大部分人初学Vue都做过一个累加器按钮

组合模式

可多个对象组合使用,也可单个对象独立使用,应用于DOM,前端组件,文件目录,部门都是典型的例子

总结

传统的设计模式目前渐渐的难以使用到我们的工作当中,设计模式并不能解决所有问题,甚至94年的那本书放在现代并不是特别好的范例,总结出抽像模式很简单那,但是真的套用到真实场景中却很困难,现在的编程语言的多编程范式会带来更多的可能性,因此更多的我们是学习设计模式中的编程思想,辩证地看待学习设计模式对我们的作用,去帮助我们写出更”好“的代码