Hi,大家好,我是拾七,今天和大家浅聊前端开发设计模式。
在前端开发领域,设计模式是一种重要的思维工具,它能够帮助开发者优化代码结构、提高可维护性,并促进团队合作。设计模式是经过长期实践和总结,解决特定问题的经验,它们为我们提供了一种在软件设计中解决常见问题的方式。本文将介绍几种常见的前端设计模式,帮助大家更好滴理解并应用它们。
MVC模式
MVC是一种经典的设计模式,它将应用程序分为三个核心部分:模型(Model)、视图(View)、控制器(Controller)。模型负责管理应用程序的数据和业务逻辑,视图负责展示数据给用户,而控制器则负责处理用户的输入并做出相应的反应。在前端开发中,通常将模型标识为数据对象或服务,视图表示为页面的HTML结构,而控制器则可以是JavaScript函数或对象。
实现一个简单的MVC模式的应用,当用户在输入框中输入内容并点击添加按钮时,数据将被添加到模型中,然后视图会自动更新以展示最新的数据。
// --定义模型
const Model = {
data: [], // 存储数据的数组
// 添加数据的方法
addData: function(item) {
this.data.push(item);
// 添加数据后触发更新视图的方法
View.render();
},
// 获取数据的方法
getData: function() {
return this.data
}
}
// --定义视图
const View = {
//渲染视图的方法
render: function() {
const dataList = Model.getData();
const listContainer = document.getElementById('list-container');
listContainer.innerHTML = ''; // 清空列表容器
dataList.forEach(item => {
const listItem = document.createElement('li');
listItem.textContent = item;
listContainer.appendChild(listItem);
})
}
}
// --定义控制器
const Controller = {
init: function() {
this.setupEventListener();
View.render(); // 初始化时渲染视图
},
// 设置事件监听的方法
setupEventListener: function() {
const addButton = document.getElementById('add-button');
addButton.addEventListener('click', function() {
const inputText = document.getElementById('input-text').value;
Model.addData(inputText); // 用户点击添加按钮时向模型添加数据
});
}
};
// 初始化控制器
Controller.init();
// --HTML结构
<div>
<input type="text" id="input-text">
<button id="add-button">Add</button>
</div>
<ul id="list-container"></ul>
观察者模式
观察者模式是一种对象行为模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知并自动更新。在前端开发中,观察者模式常被用于实现事件监听和订阅-发布模式。例如,当用户点击按钮时,所有注册了点击事件的观察者将收到通知并执行相应的操作。
如上图可知,观察者模式就是观察者和被观察者之间的通信。代码实现如下,观察者主动申请加入被观察者列表,被观察者主动将观察者加入列表:
// ===观察者模式
class Observer {
constructor(name, subject) {
this.name = name;
if(subject) {
subject.addObserver(this)
}
}
notified(message) {
console.log(this.name, 'got message', message)
}
}
// ===被观察者模式
class Subject {
constructor() {
this.observerList = [];
}
addObserver(observer) {
this.observerList.push(observer);
}
removeObserver(observer) {
const index = this.observerList.findIndex(o => o.name === observer.name);
this.observerList.splice(index, 1);
}
notifyObservers(message) {
const observers = this.observerList;
observers.forEach(o => o.notified(message))
}
}
// ==使用代码如下
const subject = new Subject();
const observerA = new Observer('observerA', subject);
const observerB = new Observer('observerB');
subject.addObserver(observerB);
subject.notifyObservers('Hello from subject');
subject.removeObserver(observerA);
subject.notifyObservers('Hello');
发布者和订阅者需要通过发布订阅中心进行关联,发布者的发布动作和订阅者的订阅动作相互独立,无需关注对方,消息派发由发布订阅中心负责。
const TYPE_A = 'music';
const TYPE_B = 'comic';
const TYPE_C = 'story';
const pubsub = new PubSub();
const publisherA = new Publisher('publisherA', pubsub);
publisherA.publish(TYPE_A, "we are young");
publisherA.publish(TYPE_B, "the silicon valley");
const subscriberA = new Subscriber("subscriberA", pubsub);
subscriberA.subscribe(TYPE_A, res => {
console.log("subscriberA received", res)
})
pubsub.notify(TYPE_A);
在观察者模式中,观察者是知道Subject的,subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在,它们只有通过消息代理进行通信。
单例模式
单例模式是一种创建模式,它确保某个类只有一个实例,并提供了一个全局访问点。在前端开发中,单例模式常被用于管理全局状态、缓存数据或创建唯一的资源管理器。例如,一个应用程序只需要一个全局的配置对象来保存配置信息,这时就可以使用单例模式确保该对象只被实例化一次。在javascript中,实现一个单例模式可以用一个变量来标识当前的类已经创建过对象,如果下次获取当前类的实例时,直接返回之前创建的对象即可,如下:
// 定义一个类
function Singleton(name) {
this.name = name;
this.instance = null;
}
// 原型扩展类的一个方法getName
Singleton.prototype.getName = function() {
console.log(this.name);
}
// 获取类的实例
Singleton.getInstance = function(name) {
if(!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
}
// 获取对象
const a = Singleton.getInstance('a');
const b = Singleton.getInstance('b');
console.log(a === b); // true
通过闭包也可以实现,如下:
// 单例构造函数
function CreateSingleton(name) {
this.name = name;
this.getName();
}
// 获取实例的名称
CreateSingleton.prototype.getName = function() {
console.log(this.name)
}
// 单例对象
const Singleton = (function() {
var instance;
return function(name) {
if(!instance) {
instance = new CreateSingleton(name);
}
return instance;
}
})();
// 创建实例对象
const a = new Singleton('a');
const b = new Singleton('b');
console.log(a === b); // true
在前端中,很多情况都是用到单例模式,例如页面存在一个模态框的时候,只有用户点击的时候才会创建,而不是加载完成之后再创建弹窗和隐藏,并且保证弹窗全局只有一个。
// === 创建一个获取对象的方法
const getSingle = function(fn) {
let result;
return function() {
return result || (result = fn.apply(this, arguments));
}
}
// === 创建弹窗
const createLoginLayer = function() {
var div = document.createElement('div');
div.innerHTML = '我是浮窗';
div.style.display = 'none';
document.body.appendChild(div);
return div;
}
const createSingleLoginLayer = getSingle(createLoginLayer);
document.getElementById('loginBtn').onClick = function() {
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
}
工厂模式
工厂模式是一种创建模式,它定义了一个用于创建对象的接口,但将具体的实现延迟到子类中。在前端开发中,工厂模式常被用于创建复杂的对象组件,从而将对象的创建和使用解耦。例如,一个UI组件可以使用工厂模式来创建不同类型的按钮或表单控件,而不必暴露具体的实现细节。工厂模式根据抽象程度不同可以分为:简单工厂模式、工厂方法模式、抽象工厂模式。
简单工厂模式也叫静态工厂模式,用一个工厂对象创建同一类对象类的实例,假设我们要开发一个公司岗位及其工作内容的录入信息,不同岗位的工作内容不一致,代码如下:
function Factory(job){
function User(job, work) {
this.job = job;
this.work = work;
}
let work;
switch(job) {
case 'coder':
work = ['写代码', '修BUG'];
return new User(job, work);
break;
case 'boss':
work = ['喝茶', '开会'];
return new User(job, work);
break;
}
}
let coder = new Factory('coder');
console.log(coder);
let boss = new Factoty('boss');
console.info(boss);
Factory就是一个简单的工厂。当我们调用工厂函数时,只需要传递name、age就可以获取到包含用户工作内容的实例对象。
工厂方法模式跟简单工厂模式差不多,但是把具体的产品放到了工厂函数的prototype中,这样以来,扩展产品种类就不必修改工厂函数了,核心类就变成抽象类,也可以随时重写某种具体的产品。
// 工厂方法
function Factory(job) {
if(this instanceof Factory) {
var a = new this[job]();
return a;
}
return new Factory(job);
}
Factory.prototype = {
'coder': function() {
this.jobName = '码农';
this.work = ['写代码', '写BUG'];
},
'boss': function() {
this.jobName = '老板';
this.work = ['喝茶', '开会'];
}
}
let coder = new Factory('coder');
console.log(coder);
let boss = new Factory('boss');
console.info(boss);
简单工厂和工厂方法模式时生产产品,那么抽象工厂模式的工作就是生产工厂的,由于javascript中并没有抽象类的概念,只能模拟、可以分成四部分:用于创建抽象类的函数、抽象类、具体类、实例化具体类。
let JobAbstractFactory = function(subType, superType) {
if(typeof JobAbstractFactory[superType] === 'function') {
function F() {};
F.prototype = new JobAbstractFactory[superType]();
subType.constructor = subType;
subType.prototype = new F();
} else {
throw new Error('抽象类不存在')
}
}
总结
设计模式是前端开发中的重要工具,它们可以帮助我们更好地组织和管理代码,并提高代码的可维护性和可扩展性。然而,在使用设计模式时需要注意不要过度设计,应根据具体的场景和需求选择合适的模式。
PS:如有改进处,欢迎在评论区留言讨论。