优化实战 第 40 期 - 代理模式最佳实战

3,292 阅读2分钟

代理模式

  • 模式定义

    代理模式 给某一个对象提供一个代理对象,并由代理对象控制对原对象的访问

    通俗的理解就是 我们生活中常见的中介

    proxy.jpeg

    通过原生 JS 的 Proxy 可以实现该设计模式

  • 模式作用

    对被代理的对象进行拦截,当被代理对象被访问时可以实现统一的处理

    增强代码的 高扩展性

  • 生活示例

    假如我想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这太浪费我得时间和精力

    我只是想买一辆车而已,为什么还要额外做这么多事情呢?于是我就通过中介公司来买车,他们给我找车源,帮我办理车辆过户流程等,我只负责选择自己喜欢的车,然后付钱就可以了

封装 fetch 请求

  • 工具方法

    判断 FormData 类型

    const isFormData = (val) => {
      return (typeof FormData !== 'undefined') && (val instanceof FormData)
    }
    

    将对象转为查询参数

    const queryString = (data = {}) => {
      return Object.keys(data).map(key=>`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`).join('&')
    }
    
  • 请求策略

    const configPolicy = Object.assign(Object.create(null), {
      GET({ url, data = {}, ...config } = options) {
        const params = queryString(data)
        return Object.assign(config, {
          url: params ? `${url}?${params}` : url,
        })
      },
      POST({ data = null, payloadType = 'json', headers = {}, ...config } = options) {
        const payload = data || (payloadType === 'json' ? JSON.stringify(data) : queryString(data))
        return Object.assign(config, {
          body: payload,
          headers: {
            ...headers,
            'Content-Type': payloadType == 'urlencoded' ? 'application/x-www-form-urlencoded; charset=utf-8' : 'application/json; charset=utf-8'
          }
        })
      }
    })
    
  • 策略匹配

    const fetch2 = async ({ prefix = '/api', method, ...options }) => {
      Object.assign(options, {
        method: method ? method.toUpperCase() : 'POST'
      })
      if (isFormData(options.data) && options.method == 'POST') {
        const { url, data, ...config } = options
        config.body = data
        return await fetch(`${prefix}${url}`, config)
      } else {
        const { url, ...config } = configPolicy[options.method](options)
        return await fetch(`${prefix}${url}`, config)
      }
    }
    
  • 代理拦截

    const handler = {
      async apply(target, ctx, [config = {}] = args) {
        const token = localStorage.getItem('AUTH-TOKEN')
        Object.assign(config, {
          'headers': {
            'Authorization': token,
          }
        })
        const res = await Reflect.apply(...arguments)
        if (res.ok) {
          const { state, msg = null, obj: data = null } = await res.json()
          return {
            state, msg, data
          }
        }
        throw new Error(res.statusText)
      }
    }
    const proxyFetch2 = new Proxy(fetch2, handler)
    
  • 注意事项

    GET 请求忽略 Content-Type 配置,直接将参数拼接至请求 URL 上即可

    POST 请求传输 FormData 类型数据时,需要删除请求头的 Content-Type 配置,让浏览器自己适配,否则会造成数据传输失败

    一起学习,加群交流看 沸点