浅浅看一下设计模式

前端开发中常用的设计模式主要分为创建型、结构型和行为型三类,下面是针对前端高频使用的模式,逐一讲解其概念、使用场景并提供可直接运行的 Demo。

一、创建型模式

这类模式主要解决「对象创建」相关的问题,让对象创建更灵活、更可控。

1. 单例模式 (Singleton)

核心概念 :保证一个类仅有一个实例,并提供一个全局访问点。 使用场景

  • 全局缓存对象 - 弹窗/模态框组件(确保页面中只有一个实例)
  • Vuex/Pinia 的 store 实例 - 全局事件总线 Demo 代码
javascript 复制代码
// 单例模式实现(通用版) 
class SingletonModal { 
    constructor(content) { 
        // 如果已有实例,直接返回 
        if (SingletonModal.instance) { 
            return SingletonModal.instance; 
        } 
        this.content = content; 
        this.element = null; 
        // 缓存实例 
        SingletonModal.instance = this; 
    } 
    // 创建弹窗 
    DOM render() { 
        if (this.element) 
        return this.element; 

        const modal = document.createElement('div'); 
        modal.style.cssText = `
            position: fixed; 
            top: 50%; 
            left: 50%; 
            transform: translate(-50%, -50%); 
            padding: 20px; 
            background: white; 
            border: 1px solid #ccc; 
            z-index: 9999; 
        `; 
        modal.textContent = this.content; 
        this.element = modal; 
        document.body.appendChild(modal); 
        return modal; 
    } 
    // 关闭弹窗 
    close() { 
        if (this.element) { 
            document.body.removeChild(this.element); 
            this.element = null; 
        } 
    } 
} 
// 测试:多次创建只会得到同一个实例 
const modal1 = new SingletonModal('这是第一个弹窗'); 
const modal2 = new SingletonModal('这是第二个弹窗'); 
console.log(modal1 === modal2); // true(验证单例) 
modal1.render(); // 渲染弹窗(内容是"这是第一个弹窗",因为实例复用) 
// modal1.close(); // 关闭弹窗 

2. 工厂模式 (Factory)

核心概念 :定义一个创建对象的接口,让子类决定实例化哪一个类,将对象创建的逻辑封装起来。 使用场景

  • 根据不同参数创建不同类型的组件(如按钮、输入框)
  • 数据格式化工具(根据数据类型返回不同格式)
  • 网络请求适配器(根据环境选择 Axios/fetch)

Demo 代码

javascript 复制代码
// 定义不同类型的组件类 
class Button { 
    constructor(text) { 
        this.text = text; 
        this.type = 'button'; 
    } 
    render() { 
        return `<button>${this.text}</button>`; 
    } 
} 
class Input { 
    constructor(placeholder) { 
        this.placeholder = placeholder; 
        this.type = 'input'; 
    } 
    render() { 
        return `<input type="text" placeholder="${this.placeholder}">`; 
    } 
} 
class Select { 
    constructor(options) { 
        this.options = options; 
        this.type = 'select'; 
    } 
    render() { 
        const optionsHtml = this.options.map(opt => `<option value="${opt.value}">${opt.label}</option>`).join(''); 
        return `<select>${optionsHtml}</select>`; 
    } 
} 
// 组件工厂类 
class ComponentFactory { 
    static createComponent(type, config) { 
        switch (type) { 
            case 'button': 
                return new Button(config.text); 
            case 'input': 
                return new Input(config.placeholder); 
            case 'select': 
                return new Select(config.options); 
            default: 
                throw new Error(`不支持的组件类型:${type}`); 
        } 
    } 
} 
// 测试:通过工厂创建不同组件 
const button = ComponentFactory.createComponent('button', { text: '提交' }); 
const input = ComponentFactory.createComponent('input', { placeholder: '请输入姓名' }); 
const select = ComponentFactory.createComponent('select', { options: [{ label: '男', value: 'male' }, { label: '女', value: 'female' }] }); 
console.log(button.render()); // <button>提交</button> 
console.log(input.render()); // <input type="text" placeholder="请输入姓名">
console.log(select.render()); // <select><option value="male">男</option><option value="female">女</option></select> 

二、结构型模式

这类模式关注对象的组合,优化类或对象的结构,提高代码的复用性和灵活性。

1. 代理模式 (Proxy)

核心概念 :为另一个对象提供一个替身或占位符,以控制对这个对象的访问。 使用场景

  • 图片懒加载(代理控制图片加载时机)
  • 权限控制(代理拦截无权限的操作)
  • 数据缓存(代理缓存重复请求的结果)
  • Vue3 的响应式原理(基于 ES6 Proxy 实现) Demo 代码(图片懒加载)
javascript 复制代码
// 真实的图片加载类 
class RealImage { 
    constructor(url) { 
        this.url = url; 
        this.loadImage(); 
    } 
    // 实际加载图片 
    loadImage() { 
        console.log(`开始加载图片:${this.url}`); 
        // 模拟图片加载耗时 
        setTimeout(() => { 
            console.log(`图片 ${this.url} 加载完成`); 
        }, 1000); 
    } 
    // 渲染图片 
    render(container) { 
        const img = document.createElement('img'); 
        img.src = this.url; 
        img.alt = '示例图片'; 
        container.appendChild(img); 
    } 
}
// 图片代理类(懒加载) 
class ImageProxy { 
    constructor(url) { 
        this.url = url; 
        this.realImage = null; 
        // 延迟创建真实图片实例 
        this.placeholder = 'https://via.placeholder.com/100x100?text=Loading'; // 占位图 
    } 
    // 代理渲染逻辑 
    render(container) { 
        // 先渲染占位图 
        const placeholderImg = document.createElement('img');
        placeholderImg.src = this.placeholder; 
        placeholderImg.alt = '加载中'; 
        container.appendChild(placeholderImg); 
        // 模拟滚动到可视区域后加载真实图片 
        setTimeout(() => { 
            this.realImage = new RealImage(this.url); 
            // 替换占位图
            container.removeChild(placeholderImg); 
            this.realImage.render(container); 
        }, 2000); 
    } 
} 
// 测试:使用代理加载图片 
const container = document.getElementById('image-container') || document.body; 
const imageProxy = new ImageProxy('https://picsum.photos/400/300');
imageProxy.render(container); 

2. 装饰器模式 (Decorator)

核心概念 :动态地给一个对象添加一些额外的职责,而不改变其原有结构。 使用场景

  • 给组件添加额外功能(如按钮添加防抖、输入框添加校验)
  • 权限装饰(给不同角色的按钮添加不同操作权限)
  • 日志装饰(给函数添加日志记录功能)

Demo 代码(函数装饰器)

javascript 复制代码
// 基础函数:提交表单 
function submitForm(data) { 
    console.log('提交表单数据:', data); 
    return { code: 200, msg: '提交成功' }; 
} 
// 装饰器1:防抖装饰 
function debounceDecorator(fn, delay = 500) { 
    let timer = null; 
    return function(...args) { 
        clearTimeout(timer); 
        timer = setTimeout(() => { fn.apply(this, args); }, delay); 
    }; 
} 
// 装饰器2:日志装饰 
function logDecorator(fn) { 
    return function(...args) { 
        console.log(`[${new Date().toLocaleString()}] 执行函数:${fn.name}`); 
        const result = fn.apply(this, args); 
        console.log(`[${new Date().toLocaleString()}] 函数执行结果:`, result); 
        return result; 
    }; 
} 
// 装饰器3:校验装饰 
function validateDecorator(fn) { 
    return function(...args) { 
        const data = args[0];
        if (!data || !data.username) { 
            console.error('校验失败:用户名不能为空'); 
            return { code: 400, msg: '用户名不能为空' }; 
        } 
        return fn.apply(this, args); 
    }; 
} 
// 组合装饰器:给提交函数添加防抖+日志+校验 
const decoratedSubmit = debounceDecorator(logDecorator(validateDecorator(submitForm)));
// 测试
decoratedSubmit({ username: '' }); // 校验失败
decoratedSubmit({ username: 'zhangsan', age: 20 }); // 防抖后执行,带日志 
decoratedSubmit({ username: 'zhangsan', age: 20 }); // 重复点击被防抖拦截 

三、行为型模式

这类模式关注对象之间的通信和交互,优化对象的行为逻辑。

1. 观察者模式 (Observer)

核心概念 :定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知并自动更新。 使用场景

  • 事件监听(如 DOM 事件、自定义事件)
  • 发布订阅系统(如 Vue 的 EventBus)
  • 状态管理(如 React 状态更新、Vue 的响应式)
  • 消息通知系统

Demo 代码(自定义事件总线)

javascript 复制代码
// 观察者模式实现(事件总线) 
class EventBus { 
    constructor() { 
        this.events = {}; // 存储事件和对应的回调函数 
    } 
    // 订阅事件 
    on(eventName, callback) { 
        if (!this.events[eventName]) { 
            this.events[eventName] = []; 
        } 
        this.events[eventName].push(callback); 
    } 
    // 取消订阅 
    off(eventName, callback) { 
        if (!this.events[eventName])
            return; 
        if (callback) { 
            // 移除指定回调 
            this.events[eventName] = this.events[eventName].filter(fn => fn !== callback); 
        } else { 
            // 清空该事件所有回调 
            this.events[eventName] = []; 
        }
    } 
    // 发布事件(触发) 
    emit(eventName, ...args) { 
        if (!this.events[eventName]) 
        return; 
        // 执行所有订阅的回调 
        this.events[eventName].forEach(callback => { callback.apply(this, args); });
    } 
    // 一次性订阅 
    once(eventName, callback) { 
        const wrapCallback = (...args) => { 
            callback.apply(this, args); 
            this.off(eventName, wrapCallback); // 执行后取消订阅 
        }; 
        this.on(eventName, wrapCallback); 
    } 
}
// 测试:使用事件总线 
const bus = new EventBus(); 

// 订阅事件 
const handleMsg = (msg) => { 
    console.log('收到消息:', msg); 
};
bus.on('message', handleMsg); 

// 一次性订阅 
bus.once('onceMsg', (msg) => { 
    console.log('一次性消息:', msg); 
});

// 发布事件 
bus.emit('message', 'Hello Observer Pattern'); // 收到消息:Hello Observer Pattern 
bus.emit('onceMsg', 'This is once'); // 一次性消息:This is once 
bus.emit('onceMsg', 'This will not be received'); // 无输出 

// 取消订阅 
bus.off('message', handleMsg); bus.emit('message', 'This will not be received'); // 无输出 

2. 策略模式 (Strategy)

核心概念 :定义一系列算法,将每个算法封装起来,并使它们可以互相替换,让算法的变化独立于使用算法的客户端。 使用场景

  • 表单校验规则(不同字段用不同校验策略)
  • 支付方式选择(支付宝/微信/银行卡)
  • 排序算法切换(快速排序/冒泡排序)
  • 价格计算策略(普通用户/VIP/超级VIP)

Demo 代码(表单校验)

javascript 复制代码
// 定义校验策略 
const validateStrategies = { 
    // 非空校验 
    required: (value, errorMsg) => { 
        if (value === '' || value === undefined || value === null) { 
            return errorMsg; 
        } 
    }, 
    // 最小长度校验 
    minLength: (value, length, errorMsg) => { 
        if (value && value.length < length) { 
            return errorMsg; 
        } 
    }, 
    // 手机号校验 
    mobile: (value, errorMsg) => { 
        const reg = /^1[3-9]\d{9}$/; 
        if (value && !reg.test(value)) { 
            return errorMsg; 
        } 
    }, 
    // 邮箱校验 
    email: (value, errorMsg) => { 
        const reg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; 
        if (value && !reg.test(value)) { 
            return errorMsg; 
        } 
    } 
}; 
// 校验器类 
class Validator { 
    constructor() { 
        this.rules = []; // 存储校验规则 
    } 
    // 添加校验规则 
    add(value, rules) { 
        rules.forEach(rule => { 
            const { strategy, errorMsg, param } = rule; 
            this.rules.push(() => { 
                // 拆分策略(如 minLength:6 拆分为 minLength 和 6) 
                const [strategyName, strategyParam] = strategy.split(':'); 
                return validateStrategies[strategyName](value, strategyParam || param, errorMsg); 
            }); 
        });
    } 
    // 执行校验 
    validate() { 
        for (const rule of this.rules) { 
            const errorMsg = rule(); 
            if (errorMsg) { 
                return errorMsg; // 有错误立即返回 
            } 
        } 
        return ''; // 校验通过 
    } 
} 
// 测试:表单校验 
const formData = { 
    username: '', 
    password: '123', 
    mobile: '1234567890', 
    email: 'test@example' 
}; 
// 创建校验器并添加规则 
const validator = new Validator(); 
validator.add(formData.username, [{ strategy: 'required', errorMsg: '用户名不能为空' }]);
validator.add(formData.password, [ { strategy: 'required', errorMsg: '密码不能为空' }, { strategy: 'minLength:6', errorMsg: '密码长度不能少于6位' } ]); 
validator.add(formData.mobile, [{ strategy: 'mobile', errorMsg: '手机号格式错误' }]);
validator.add(formData.email, [{ strategy: 'email', errorMsg: '邮箱格式错误' }]); 
// 执行校验 
const errorMsg = validator.validate(); 
console.log(errorMsg); // 用户名不能为空(第一个错误) 

总结

前端开发中高频使用的设计模式及核心要点:

  1. 单例模式:保证唯一实例,适用于全局组件/缓存/事件总线,核心是缓存实例并复用。
  2. 工厂模式:封装对象创建逻辑,适用于多类型组件/工具创建,核心是根据参数返回不同实例。
  3. 代理模式:控制对象访问,适用于懒加载/权限控制/缓存,核心是「替身」拦截并处理逻辑。
  4. 装饰器模式:动态扩展功能,适用于函数增强/组件扩展,核心是不修改原对象仅添加职责。
  5. 观察者模式:一对多通知,适用于事件系统/状态管理,核心是发布-订阅的解耦通信。
  6. 策略模式 :算法封装与替换,适用于校验/支付/排序,核心是将算法与使用逻辑分离。 这些模式的核心价值是解耦、复用、可扩展,实际开发中不必生搬硬套,而是根据场景灵活运用,比如 Vue/React 框架内部就大量使用了这些模式(如 React 的合成事件用了观察者模式,Vue3 的响应式用了代理模式)。
相关推荐
Lee川1 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix2 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人2 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl2 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人2 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼2 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端
布列瑟农的星空2 小时前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust
Mr Xu_2 小时前
Vue 3 中计算属性的最佳实践:提升可读性、可维护性与性能
前端·javascript