青训营笔记 | 前端开发中常见的设计模式 | 豆包MarsCode AI刷题

9 阅读6分钟

前端开发中,设计模式(Design Patterns)是指在开发过程中针对特定问题的通用解决方案。

理解这些设计模式及其优缺点有助于我们更高效地构建、维护和扩展前端应用。

以下是一些常见前端开发中的设计模式


1. MVC(Model-View-Controller)模式

概念

MVC 将应用程序分为三部分:

  • Model:负责管理数据和业务逻辑。
  • View:负责显示数据和用户交互。
  • Controller:负责处理用户输入,并将其转化为对 Model 的操作。

案例

AngularJS 使用了类似于 MVC 的架构:

  • Model:与 Angular 的 $scopeservice 配合。
  • View:HTML 模板中通过数据绑定展示。
  • Controller:用控制器处理输入并更新模型。
// AngularJS 示例
app.controller('MainCtrl', function($scope) {
    $scope.message = "Hello, MVC!";
    $scope.updateMessage = function(newMessage) {
        $scope.message = newMessage;
    };
});

优点

  • 关注点分离,代码更易于维护。
  • View 和 Model 可以独立开发。

缺点

  • 在前端复杂应用中,Controller 容易变得臃肿。
  • 不适用于动态交互非常频繁的现代单页应用。

2. MVVM(Model-View-ViewModel)模式

概念

MVVM 是 MVC 的改进,移除了 Controller,用 ViewModel 代替:

  • Model:与 MVC 相同。
  • View:负责用户界面。
  • ViewModel:作为 View 和 Model 的中介,处理数据绑定逻辑。

案例

Vue.js 和 Angular 2+ 是典型的 MVVM 框架:

  • Vue 的 datacomputed 属性就是 ViewModel 的核心部分。
// Vue 示例
const app = new Vue({
    el: '#app',
    data: {
        message: "Hello, MVVM!"
    },
    computed: {
        reversedMessage() {
            return this.message.split('').reverse().join('');
        }
    }
});

优点

  • 双向数据绑定(如 Vue 的 v-model)大大简化了开发。
  • View 和业务逻辑解耦,提高可维护性。

缺点

  • 双向绑定的性能开销较大。
  • 在大型应用中,复杂的状态管理可能导致代码难以调试。

React 与 MVVM 模式也非常契合:

  • Model:状态(通常是 useState 或 Redux 管理的状态)。
  • View:React 组件的 JSX。
  • ViewModel:通过 useMemouseEffect 等 Hook 提供中间逻辑。
import React, { useState, useMemo } from "react";

const ReversedMessage = () => {
  // Model
  const [message, setMessage] = useState("Hello, MVVM!");

  // ViewModel
  const reversedMessage = useMemo(() => message.split("").reverse().join(""), [message]);

  // View
  return (
    <div>
      <h1>Original: {message}</h1>
      <h1>Reversed: {reversedMessage}</h1>
      <input value={message} onChange={(e) => setMessage(e.target.value)} />
    </div>
  );
};

export default ReversedMessage;

优点

  • 使用 useMemo 等优化计算逻辑,简化了 View 的复杂性。
  • 清晰分离了状态管理和 UI 渲染逻辑。

缺点

  • 中间层(ViewModel)增加了复杂性。
  • 在大型应用中,双向绑定可能引入性能开销。

3. Flux 和 Redux 模式

概念

Flux 是一种单向数据流模式,Redux 是其改进版本:

  • Action:描述事件。
  • Dispatcher:分发事件。
  • Store:存储应用状态。
  • View:根据 Store 中的数据渲染 UI。

案例

React + Redux 是常见的使用组合:

// Redux 示例
const initialState = { count: 0 };

function reducer(state = initialState, action) {
    switch (action.type) {
        case 'INCREMENT':
            return { count: state.count + 1 };
        case 'DECREMENT':
            return { count: state.count - 1 };
        default:
            return state;
    }
}

const store = Redux.createStore(reducer);
store.dispatch({ type: 'INCREMENT' });
console.log(store.getState()); // { count: 1 }

优点

  • 状态管理集中化,方便调试和预测行为。
  • 提供时间旅行调试和状态回滚功能。

缺点

  • 需要编写大量样板代码(boilerplate)。
  • 学习曲线较高,对初学者不友好。

4. 观察者模式

概念

观察者模式允许对象(观察者)订阅另一个对象(被观察者)的状态变化,状态变化会通知所有订阅者。

案例

  • RxJS 和 Vue 的 watch 是观察者模式的经典实现。
// Vue 中的 watch 示例
new Vue({
    data: {
        counter: 0
    },
    watch: {
        counter(newValue) {
            console.log('Counter changed:', newValue);
        }
    }
});

优点

  • 简化了事件处理流程。
  • 解耦了观察者和被观察者,提高了代码灵活性。

缺点

  • 如果观察者过多,可能导致性能问题。
  • 状态更新链容易变得复杂和难以追踪。

5. 模板模式

概念

模板模式定义了一种操作的骨架,但允许子类重写具体步骤。

案例

  • React 的函数组件和钩子(Hooks)可以看作模板模式的变种。
// React 示例
// 自定义 Hook 实现模板模式
import React, { useState } from "react";

const useCounter = (initialValue = 0) => {
  const [count, setCount] = useState(initialValue);

  const increment = () => setCount((prev) => prev + 1);
  const decrement = () => setCount((prev) => prev - 1);

  return { count, increment, decrement };
};

const Counter = () => {
  const { count, increment, decrement } = useCounter(10); // 模板逻辑复用

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export default Counter;

优点

  • 提高代码复用性。
  • 提供一致性和可扩展性。

缺点

  • 子类过多时,可能造成代码管理困难。
  • 模板可能过于复杂,增加维护成本。

6. 工厂模式

概念

工厂模式通过一个工厂函数创建对象,而不是直接实例化对象。

案例

在 Vue 和 React 中创建组件实例常用工厂模式:

// React 工厂模式示例
const ButtonFactory = ({ type, label }) => {
  switch (type) {
    case "primary":
      return <button style={{ backgroundColor: "blue", color: "white" }}>{label}</button>;
    case "secondary":
      return <button style={{ backgroundColor: "gray", color: "black" }}>{label}</button>;
    default:
      return <button>{label}</button>;
  }
};

const App = () => (
  <div>
    <ButtonFactory type="primary" label="Primary Button" />
    <ButtonFactory type="secondary" label="Secondary Button" />
  </div>
);

export default App;

优点

  • 解耦对象的创建和使用。
  • 易于扩展新类型。

缺点

  • 工厂函数可能变得臃肿。
  • 如果创建逻辑复杂,代码可读性可能降低。

7. 策略模式

定义:定义一系列算法,将每个算法封装起来,并让它们可以互换。

对应React:利用函数传递不同的策略来实现某种行为。

// 策略函数
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

// 使用策略
const Calculator = ({ strategy, a, b }) => <h1>Result: {strategy(a, b)}</h1>;

const App = () => (
  <div>
    <Calculator strategy={add} a={2} b={3} /> {/* 加法策略 */}
    <Calculator strategy={multiply} a={2} b={3} /> {/* 乘法策略 */}
  </div>
);

优点

  • 避免了多重 if-else 判断,提高灵活性。
  • 易于扩展新策略。

缺点

  • 策略类(或函数)数量可能过多,管理复杂。

8. 代理模式

定义:为某对象提供一个代理,以控制对这个对象的访问。

对应 React:在高阶组件(HOC)或 useMemo 中为组件或数据提供代理访问。

// 简单的 HOC 示例
const withLogging = (WrappedComponent) => (props) => {
  console.log("Component rendered with props:", props);
  return <WrappedComponent {...props} />;
};

// 使用代理
const HelloWorld = (props) => <h1>Hello, {props.name}!</h1>;
const LoggedHelloWorld = withLogging(HelloWorld);

const App = () => <LoggedHelloWorld name="React" />;

优点

  • 控制对象访问(如日志记录、性能优化)。
  • 实现某些额外功能而不修改原对象。

缺点

  • 增加代码复杂度,可能引入不必要的中间层。

9. 中介者模式

定义:通过一个中介对象来封装对象间的交互,减少对象之间的耦合。

对应 ReactContext API 可以充当中介者,协调多个子组件之间的状态共享。

import React, { createContext, useContext, useState } from "react";

// 中介者 Context
const ThemeContext = createContext();

const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState("light");
  const toggleTheme = () => setTheme((prev) => (prev === "light" ? "dark" : "light"));
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

const Toolbar = () => <Button />;

const Button = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);
  return (
    <button onClick={toggleTheme} style={{ background: theme === "light" ? "#fff" : "#333" }}>
      Toggle Theme
    </button>
  );
};

const App = () => (
  <ThemeProvider>
    <Toolbar />
  </ThemeProvider>
);

优点

  • 减少组件间的直接耦合。
  • 提高模块化程度,逻辑清晰。

缺点

  • 中介者过于复杂时会成为新的复杂中心。

10. 装饰者模式

定义:动态地给对象添加职责,而不改变其结构。
React 对应:高阶组件(HOC)是装饰者模式的典型实现,或者使用 useRef 装饰 DOM 元素。

// 高阶组件实现装饰者模式
const withBorder = (WrappedComponent) => (props) => (
  <div style={{ border: "2px solid red" }}>
    <WrappedComponent {...props} />
  </div>
);

const HelloWorld = (props) => <h1>Hello, {props.name}!</h1>;
const BorderedHelloWorld = withBorder(HelloWorld);

const App = () => <BorderedHelloWorld name="React" />;

优点

  • 动态扩展功能而不影响原组件。
  • 灵活性强,易于组合多个装饰者。

缺点

  • 可能出现嵌套过深的问题,难以调试。