代理模式核心概念

  1. 代理模式 (Proxy Pattern)
    1. 是一种结构型设计模式,核心思想是为一个对象提供一个代理对象,并由代理对象控制对原对象的访问;
    2. 代理对象可以在不修改原对象的前提下,对原对象的访问进行拦截、增强或控制 (比如权限校验、缓存、懒加载等)
    3. 代理模式分为几种常见类型:
      1. 静态代理:代理类在编译期就确定,和原对象一一对应;
      2. 动态代理:代理类在运行时动态生成 (如 JS/TS 中的 Proxy 对象)
      3. 保护代理:控制对原对象的访问权限;
      4. 虚拟代理:延迟创建开销大的原对象 (懒加载)
  2. 类图:
  3. 实现代码:
    // 1. 抽象主题(Subject):定义统一接口
    interface ImageSubject {
      // 核心方法:显示图片
      display(): void;
    }
    
    // 2. 真实主题(RealSubject):实际处理图片加载的类
    class RealImage implements ImageSubject {
      private imageUrl: string;
    
      // 构造函数:加载图片(模拟耗时操作)
      constructor(url: string) {
        this.imageUrl = url;
        this.loadImageFromServer(); // 实例化时就会加载图片(开销大)
      }
    
      // 模拟从服务器加载图片(耗时操作)
      private loadImageFromServer(): void {
        console.log(`[RealImage] 正在加载图片:${this.imageUrl}`);
        // 模拟网络延迟
        setTimeout(() => {
          console.log(`[RealImage] 图片 ${this.imageUrl} 加载完成`);
        }, 1000);
      }
    
      // 实现接口:显示图片
      display(): void {
        console.log(`[RealImage] 显示图片:${this.imageUrl}`);
      }
    }
    
    // 3. 代理(Proxy):控制 RealImage 的访问,添加额外逻辑
    class ImageProxy implements ImageSubject {
      private imageUrl: string;
      // 延迟初始化 RealImage(核心:只有需要时才创建)
      private realImage: RealImage | null = null;
    
      constructor(url: string) {
        this.imageUrl = url;
      }
    
      // 实现接口:显示图片(代理逻辑)
      display(): void {
        // 1. 代理的前置增强:显示占位符
        console.log(`[ImageProxy] 显示占位符(加载中...)`);
    
        // 2. 懒加载:只有首次调用 display 时才创建 RealImage
        if (!this.realImage) {
          this.realImage = new RealImage(this.imageUrl);
        }
    
        // 3. 调用真实主题的核心方法
        this.realImage.display();
    
        // 4. 代理的后置增强:记录访问日志
        console.log(`[ImageProxy] 记录图片 ${this.imageUrl} 的访问日志`);
      }
    }
    
    // ---------------------- 测试代码 ----------------------
    // 使用代理访问真实图片(而非直接实例化 RealImage)
    const imageProxy = new ImageProxy("https://example.com/photo.jpg");
    
    // 首次调用 display:触发 RealImage 的创建和加载
    console.log("=== 首次调用 display ===");
    imageProxy.display();
    
    // 第二次调用 display:复用已创建的 RealImage(不再重复加载)
    console.log("\n=== 第二次调用 display ===");
    imageProxy.display();
    

常用使用场景

虚拟代理:懒加载(最常用)

  1. 场景:图片、大组件 (如富文本编辑器、图表) 的懒加载,只有当元素进入视口 / 被触发时才加载资源,减少初始加载开销;

  2. 示例:上述代码中的图片懒加载,或 React/Vue 中路由组件的懒加载 (React.lazy() 本质就是虚拟代理)

保护代理:权限控制

  1. 场景:控制对敏感方法 / 数据的访问,比如:

    1. 管理后台中,不同权限的用户只能调用对应接口;
    2. 限制普通用户修改全局配置,只有管理员可操作;
  2. 实现代码:

    // 真实的配置管理类
    class ConfigManager {
      updateConfig(key: string, value: any) {
        console.log(`更新配置:${key}=${value}`);
      }
    }
    
    // 权限代理
    class ConfigProxy {
      private realManager = new ConfigManager();
      private userRole: string;
    
      constructor(role: string) {
        this.userRole = role;
      }
    
      updateConfig(key: string, value: any) {
        if (this.userRole === "admin") {
          this.realManager.updateConfig(key, value); // 管理员允许访问
        } else {
          console.error("无权限:普通用户无法修改配置");
        }
      }
    }
    
    // 测试
    const adminProxy = new ConfigProxy("admin");
    adminProxy.updateConfig("theme", "dark"); // 正常执行
    
    const userProxy = new ConfigProxy("user");
    userProxy.updateConfig("theme", "dark"); // 报错:无权限
    

缓存代理:复用计算 / 请求结果

  1. 场景:对耗时的计算、重复的接口请求做缓存,避免重复执行:

    1. 复杂的列表筛选 / 排序计算;
    2. 无状态的 GET 请求 (如获取商品详情)
  2. 实现代码:

    // 真实的计算类
    class Calculator {
      // 模拟耗时计算:求和
      sum(arr: number[]): number {
        console.log("执行耗时计算...");
        return arr.reduce((a, b) => a + b, 0);
      }
    }
    
    // 缓存代理
    class CalculatorProxy {
      private realCalc = new Calculator();
      private cache = new Map<string, number>(); // 缓存结果
    
      sum(arr: number[]): number {
        const key = arr.join(","); // 生成缓存 key
        if (this.cache.has(key)) {
          console.log("使用缓存结果");
          return this.cache.get(key)!;
        }
        const result = this.realCalc.sum(arr);
        this.cache.set(key, result); // 缓存结果
        return result;
      }
    }
    
    // 测试
    const proxy = new CalculatorProxy();
    console.log(proxy.sum([1,2,3])); // 执行计算,输出 6
    console.log(proxy.sum([1,2,3])); // 使用缓存,输出 6
    

原生 Proxy:对象拦截(动态代理)

  1. 场景:Vue3 的响应式系统 (reactive)、数据校验、日志记录、防抖 / 节流等;

  2. 实现代码:

    // 代理对象,拦截属性的读取和修改
    const reactive = (target: object) => {
      return new Proxy(target, {
        get(target, key) {
          console.log(`读取属性 ${key.toString()}`);
          return Reflect.get(target, key);
        },
        set(target, key, value) {
          console.log(`修改属性 ${key.toString()} 为 ${value}`);
          return Reflect.set(target, key, value);
        },
      });
    };
    
    // 测试
    const user = reactive({ name: "张三", age: 20 });
    user.name; // 输出:读取属性 name
    user.age = 21; // 输出:修改属性 age 为 21
    

代理模式优缺点

优点

  1. 职责清晰:真实对象专注于业务逻辑;

  2. 高扩展性:无需修改真实对象即可增加功能;

  3. 智能化:可以实现智能访问控制;

缺点

  1. 增加复杂度:引入额外代理类;

  2. 性能影响:可能增加响应延迟;

  3. 可能造成混淆:真实对象和代理对象的关系;

打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

你好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 代理模式核心概念
  2. 2. 常用使用场景
    1. 2.1. 虚拟代理:懒加载(最常用)
    2. 2.2. 保护代理:权限控制
    3. 2.3. 缓存代理:复用计算 / 请求结果
    4. 2.4. 原生 Proxy:对象拦截(动态代理)
  3. 3. 代理模式优缺点
    1. 3.1. 优点
    2. 3.2. 缺点