代理模式在前端的运用

153 阅读2分钟

反向代理

image.png

与一般访问流程相比,使用反向代理后,直接收到请求的服务器是代理服务器,然后将请求转发给内部网络上真正进行处理的服务器,得到的结果返回给客户端。反向代理隐藏了真实的服务器,为服务器收发请求,使真实服务器对客户端不可见。而与此对应的正向代理,使真实客户端对服务器不可见

拦截器

拦截器是动态代理的方便写法,将想要插入的逻辑利用我们定义的拦截器接口抛给外部实现。动态代理,可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作

我们在项目中经常使用 Axios 的实例来进行 HTTP 的请求,使用拦截器 interceptor 可以提前对 request 请求和 response 返回进行一些预处理,还有 vue-router、react-router 路由跳转的拦截器,可以进行一些路由跳转的预处理等操作

interface ConfigType {
  message: string
}

type Fun = (value: ConfigType) => ConfigType

interface InterceptorObj {
  resolved: Fun;
  rejected?: Fun
}

type InterceptorFun = (resolved: Fun, rejected?: Fun) => void

interface AxiosType {
  interceptors: {
    request: Array<InterceptorObj>;
    response: Array<InterceptorObj>;
  };
  useRequestInterceptor: InterceptorFun;
  useResponseInterceptor: InterceptorFun;
  run: (config: ConfigType) => Promise<ConfigType>;
}

class Axios implements AxiosType {
  interceptors: {
    request: Array<InterceptorObj>;
    response: Array<InterceptorObj>;
  };
  constructor() {
    this.interceptors = {
      request: [],
      response: [],
    };
  }
  // 注册请求拦截器
  useRequestInterceptor(resolved: Fun, rejected?: Fun) {
    this.interceptors?.request.push({ resolved, rejected });
  }
  // 注册响应拦截器
  useResponseInterceptor(resolved: Fun, rejected?: Fun) {
    this.interceptors?.response.push({ resolved, rejected });
  }

  run(config: ConfigType) {
    const chain: Array<InterceptorObj> = [];

    // 把请求拦截器往数组头部推
    this.interceptors?.request.forEach((interceptor: InterceptorObj) => {
      chain.unshift(interceptor);
    });

    // 把响应拦截器往数组尾部推
    this.interceptors?.response.forEach((interceptor: InterceptorObj) => {
      chain.push(interceptor);
    });

    let promise = Promise.resolve(config);
    
    // 利用promise.then的能力递归执行所有的拦截器
    while (chain.length > 0) {
      const current = chain.shift()
      // const { resolved, rejected } = current;
      promise = promise.then(current?.resolved, current?.rejected);
    }
    //响应拦截器处理过后的promise
    return promise;
  }
}

前端框架的数据响应式化

要实现数据的响应式变化,我们需要拦截一个对象的读取和设置操作,在读取(get、has、ownKeys)时收集依赖,在设置时(deleteProperty、set)时触发依赖。

function reactive(obj: any, isShallow = false): boolean {

  return new Proxy(obj, {
    get(target, key, receiver) {
      // 收集依赖
      track(target, key);
      const result = Reflect.get(target, key, receiver);
      if (isShallow) {
        return result;
      }
      if (typeof result === "object" && result !== null) {
        return reactive(result);
      }
      return result;
    },
    
    has(target, key) {
      // 收集依赖
      track(target, key);
      return Reflect.has(target, key);
    },
    
    ownKeys(target) {
      // 收集依赖
      track(target, KEY);//KEY由Symbol()生成
      return Reflect.ownKeys(target);
    },
    
    deleteProperty(target, key) {
      const hadKey = Object.prototype.hasOwnProperty.call(target, key);
      const result = Reflect.deleteProperty(target, key);
      if (hadKey && result) {
         // 触发依赖
        trigger(target, key, "DELETE");
      }
      return result;
    },
    
    set(target, key, newVal, receiver) {
      const type = Object.prototype.hasOwnProperty.call(target, key)
        ? "SET"
        : "ADD";
      const oldVal = target[key];
      const result = Reflect.set(target, key, newVal, receiver);
      if (oldVal !== newVal) {
        // 触发依赖
        trigger(target, key, type);
      }
      return result;
    },
  });
}

缓存代理

备忘模式就是使用缓存代理的思想,将复杂计算的结果缓存起来,下次传参一致时直接返回之前缓存的计算结果。