详解前端框架中的设计模式 | 豆包MarsCode AI刷题

37 阅读3分钟

前端中的核心设计模式详解

1. 单例模式

单例模式确保一个类只有一个实例,并提供一个全局访问点。

基础实现

class Singleton {
  private static instance: Singleton;
  private constructor() {} // 私有构造函数

  public static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }
}

实际应用:全局状态管理

class Store {
  private static instance: Store;
  private state: Record<string, any> = {};

  private constructor() {}

  static getInstance(): Store {
    if (!Store.instance) {
      Store.instance = new Store();
    }
    return Store.instance;
  }

  setState(key: string, value: any) {
    this.state[key] = value;
  }

  getState(key: string) {
    return this.state[key];
  }
}

// 使用示例
const store1 = Store.getInstance();
const store2 = Store.getInstance();
console.log(store1 === store2); // true

2. 发布订阅模式

发布订阅模式允许对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知。

基础实现

class EventEmitter {
  private events: Map<string, Function[]> = new Map();

  // 订阅
  subscribe(event: string, callback: Function) {
    if (!this.events.has(event)) {
      this.events.set(event, []);
    }
    this.events.get(event)!.push(callback);
  }

  // 发布
  publish(event: string, data?: any) {
    if (this.events.has(event)) {
      this.events.get(event)!.forEach(callback => callback(data));
    }
  }

  // 取消订阅
  unsubscribe(event: string, callback: Function) {
    if (this.events.has(event)) {
      const callbacks = this.events.get(event)!;
      this.events.set(event, callbacks.filter(cb => cb !== callback));
    }
  }
}

实际应用:组件通信

// 创建事件总线
const eventBus = new EventEmitter();

// 组件A:发布消息
function ComponentA() {
  const sendMessage = () => {
    eventBus.publish('message', 'Hello from A!');
  };
}

// 组件B:订阅消息
function ComponentB() {
  useEffect(() => {
    const handleMessage = (message: string) => {
      console.log(message);
    };
    
    eventBus.subscribe('message', handleMessage);
    return () => eventBus.unsubscribe('message', handleMessage);
  }, []);
}

3. 原型模式

原型模式通过克隆现有对象来创建新对象,而不是通过实例化类。

基础实现

interface Cloneable {
  clone(): this;
}

class Component implements Cloneable {
  constructor(public name: string, public config: any) {}

  clone(): this {
    return Object.create(
      Object.getPrototypeOf(this),
      Object.getOwnPropertyDescriptors(this)
    );
  }
}

实际应用:组件复用

class UIComponent implements Cloneable {
  constructor(
    public type: string,
    public styles: Record<string, string>,
    public children: UIComponent[] = []
  ) {}

  clone(): this {
    const clone = Object.create(Object.getPrototypeOf(this));
    clone.type = this.type;
    clone.styles = { ...this.styles };
    clone.children = this.children.map(child => child.clone());
    return clone;
  }
}

// 使用示例
const button = new UIComponent('button', { color: 'blue' });
const clonedButton = button.clone();

4. 代理模式(Proxy Pattern)

代理模式为其他对象提供一种代理以控制对这个对象的访问。

基础实现

// 使用ES6 Proxy
const handler = {
  get(target: any, property: string) {
    console.log(`访问属性: ${property}`);
    return target[property];
  },
  set(target: any, property: string, value: any) {
    console.log(`设置属性: ${property} = ${value}`);
    target[property] = value;
    return true;
  }
};

const target = { name: 'target' };
const proxy = new Proxy(target, handler);

实际应用:数据响应式

function reactive<T extends object>(obj: T): T {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key); // 追踪依赖
      return Reflect.get(target, key);
    },
    set(target, key, value) {
      const result = Reflect.set(target, key, value);
      trigger(target, key); // 触发更新
      return result;
    }
  });
}

// 使用示例
const state = reactive({ count: 0 });

5. 迭代器模式

迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素。

基础实现

class Iterator<T> {
  private index = 0;
  
  constructor(private items: T[]) {}
  
  hasNext(): boolean {
    return this.index < this.items.length;
  }
  
  next(): T | null {
    return this.hasNext() ? this.items[this.index++] : null;
  }
  
  current(): T | null {
    return this.items[this.index] || null;
  }
  
  reset() {
    this.index = 0;
  }
}

实际应用:虚拟列表

class VirtualList<T> {
  private iterator: Iterator<T>;
  
  constructor(private items: T[], private pageSize: number = 10) {
    this.iterator = new Iterator(items);
  }
  
  getPage(): T[] {
    const result: T[] = [];
    let count = 0;
    
    while (this.iterator.hasNext() && count < this.pageSize) {
      const item = this.iterator.next();
      if (item) {
        result.push(item);
        count++;
      }
    }
    
    return result;
  }
  
  reset() {
    this.iterator.reset();
  }
}

// 使用示例
const list = new VirtualList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3);
console.log(list.getPage()); // [1, 2, 3]
console.log(list.getPage()); // [4, 5, 6]

6. 组合模式

组合模式允许你将对象组合成树形结构来表现"整体/部分"层次结构,使得客户以一致的方式处理单个对象和对象的组合。

基础实现

// 抽象组件
interface Component {
  name: string;
  add?(component: Component): void;
  remove?(component: Component): void;
  getChild?(index: number): Component | null;
  operation(): void;
}

// 叶子节点
class Leaf implements Component {
  constructor(public name: string) {}
  
  operation(): void {
    console.log(`Leaf ${this.name} operation`);
  }
}

// 复合节点
class Composite implements Component {
  private children: Component[] = [];
  
  constructor(public name: string) {}
  
  add(component: Component): void {
    this.children.push(component);
  }
  
  remove(component: Component): void {
    const index = this.children.indexOf(component);
    if (index > -1) {
      this.children.splice(index, 1);
    }
  }
  
  getChild(index: number): Component | null {
    return this.children[index] || null;
  }
  
  operation(): void {
    console.log(`Composite ${this.name} operation`);
    this.children.forEach(child => child.operation());
  }
}

实际应用:UI组件树

// UI组件基类
interface UIComponent {
  render(): void;
  add?(child: UIComponent): void;
  remove?(child: UIComponent): void;
  getChildren?(): UIComponent[];
}

// 基础组件(叶子节点)
class Button implements UIComponent {
  constructor(private text: string) {}
  
  render(): void {
    console.log(`Rendering Button: ${this.text}`);
  }
}

class Input implements UIComponent {
  constructor(private placeholder: string) {}
  
  render(): void {
    console.log(`Rendering Input: ${this.placeholder}`);
  }
}

// 容器组件(复合节点)
class Form implements UIComponent {
  private children: UIComponent[] = [];
  
  constructor(private name: string) {}
  
  add(child: UIComponent): void {
    this.children.push(child);
  }
  
  remove(child: UIComponent): void {
    const index = this.children.indexOf(child);
    if (index > -1) {
      this.children.splice(index, 1);
    }
  }
  
  getChildren(): UIComponent[] {
    return this.children;
  }
  
  render(): void {
    console.log(`Rendering Form: ${this.name}`);
    this.children.forEach(child => child.render());
  }
}

// 使用示例
const loginForm = new Form('Login');
loginForm.add(new Input('Username'));
loginForm.add(new Input('Password'));
loginForm.add(new Button('Submit'));

loginForm.render();
// 输出:
// Rendering Form: Login
// Rendering Input: Username
// Rendering Input: Password
// Rendering Button: Submit

实际应用:虚拟DOM实现

interface VNode {
  type: string;
  props: Record<string, any>;
  children: (VNode | string)[];
  render(): void;
}

class VElement implements VNode {
  constructor(
    public type: string,
    public props: Record<string, any>,
    public children: (VNode | string)[] = []
  ) {}
  
  render(): void {
    console.log(`<${this.type} ${Object.entries(this.props).map(([key, value]) => 
      `${key}="${value}"`).join(' ')}>`);
    this.children.forEach(child => {
      if (typeof child === 'string') {
        console.log(child);
      } else {
        child.render();
      }
    });
    console.log(`</${this.type}>`);
  }
}

// 使用示例
const app = new VElement('div', { class: 'container' }, [
  new VElement('h1', {}, ['Hello']),
  new VElement('p', { class: 'content' }, [
    'This is a ',
    new VElement('strong', {}, ['composite']),
    ' pattern example'
  ])
]);

app.render();

组合模式在React中的应用

// React组件树示例
interface Props {
  children?: React.ReactNode;
}

// 容器组件
const Container: React.FC<Props> = ({ children }) => {
  return (
    <div className="container">
      {children}
    </div>
  );
};

// 复合组件
const Section: React.FC<Props & { title: string }> = ({ title, children }) => {
  return (
    <section>
      <h2>{title}</h2>
      {children}
    </section>
  );
};

// 使用示例
function App() {
  return (
    <Container>
      <Section title="Section 1">
        <p>Content 1</p>
      </Section>
      <Section title="Section 2">
        <p>Content 2</p>
        <Section title="Nested Section">
          <p>Nested Content</p>
        </Section>
      </Section>
    </Container>
  );
}

组合模式的优缺点

优点:

  1. 简化了客户端代码,客户端可以一致地处理单个对象和组合对象
  2. 使得添加新类型的组件变得容易
  3. 提供了清晰的层次结构

缺点:

  1. 可能使设计变得太过一般化
  2. 在某些情况下很难限制组合中的组件类型
  3. 可能需要处理组件遍历的性能问题

总结

各模式的适用场景

  1. 单例模式:全局状态管理、配置管理
  2. 发布订阅模式:组件通信、事件处理
  3. 原型模式:对象复制、组件克隆
  4. 代理模式:数据响应式、权限控制
  5. 迭代器模式:数据遍历、分页处理
  6. 组合模式:构建组件树、处理层次结构