基於小程序請求接口 wx.request 封裝的類 axios 請求

基於小程序請求接口 wx.request 封裝的類 axios 請求

Introduction

feature

  • 支持 wx.request 所有配置項
  • 支持 axios 調用方式
  • 支持 自定義 baseUrl
  • 支持 自定義響應狀態碼對應 resolve 或 reject 狀態
  • 支持 對響應(resolve/reject)分別做統一的額外處理
  • 支持 轉換請求數據和響應數據
  • 支持 請求緩存(內存或本地緩存),可設置緩存標記、過期時間

use

app.js @onLaunch

  import axios form 'axios'
  axios.creat({
    header: {
      content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
    },
    baseUrl: '//api.baseurl.com',
    ...
  });

page.js

axios
  .post("/url", { id: 123 })
  .then((res) => {
    console.log(response);
  })
  .catch((err) => {
    console.log(err);
  });

API

  axios(config) - 默認get
  axios(url[, config]) - 默認get
  axios.get(url[, config])
  axios.post(url[, data[, config]])
  axios.cache(url[, data[, config]]) - 緩存請求(內存)
  axios.cache.storage(url[, data[, config]]) - 緩存請求(內存 & local storage)
  axios.creat(config) - 初始化定製配置,覆蓋默認配置

config

默認配置項說明

export default {
  // 請求接口地址
  url: undefined,
  // 請求的參數
  data: {},
  // 請求的 header
  header: "application/json",
  // 超時時間,單位為毫秒
  timeout: undefined,
  // HTTP 請求方法
  method: "GET",
  // 返回的數據格式
  dataType: "json",
  // 響應的數據類型
  responseType: "text",
  // 開啟 http2
  enableHttp2: false,
  // 開啟 quic
  enableQuic: false,
  // 開啟 cache
  enableCache: false,

  /** 以上為wx.request的可配置項,參考 //developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html */
  /** 以下為wx.request沒有的新增配置項 */

  // {String} baseURL` 將自動加在 `url` 前面,可以通過設置一個 `baseURL` 便於傳遞相對 URL
  baseUrl: "",
  // {Function} (同axios的validateStatus)定義對於給定的HTTP 響應狀態碼是 resolve 或 reject  promise 。如果 `validateStatus` 返回 `true` (或者設置為 `null` 或 `undefined`),promise 將被 resolve; 否則,promise 將被 reject
  validateStatus: undefined,
  // {Function} 請求參數包裹(類似axios的transformRequest),通過它可統一補充請求參數需要的額外信息(appInfo/pageInfo/場景值...),需return data
  transformRequest: undefined,
  // {Function} resolve狀態下響應數據包裹(類似axios的transformResponse),通過它可統一處理響應數據,需return res
  transformResponse: undefined,
  // {Function} resolve狀態包裹,通過它可做接口resolve狀態的統一處理
  resolveWrap: undefined,
  // {Function} reject狀態包裹,通過它可做接口reject狀態的統一處理
  rejectWrap: undefined,
  // {Boolean} _config.useCache 是否開啟緩存
  useCache: false,
  // {String} _config.cacheName 緩存唯一key值,默認使用url&data生成
  cacheName: undefined,
  // {Boolean} _config.cacheStorage 是否開啟本地緩存
  cacheStorage: false,
  // {Any} _config.cacheLabel 緩存標誌,請求前會對比該標誌是否變化來決定是否使用緩存,可用useCache替代
  cacheLabel: undefined,
  // {Number} _config.cacheExpireTime 緩存時長,計算緩存過期時間,單位-秒
  cacheExpireTime: undefined,
};

實現

axios.js

import Axios from "./axios.class.js";

// 創建axios實例
const axiosInstance = new Axios();
// 獲取基礎請求axios
const { axios } = axiosInstance;
// 將實例的方法bind到基礎請求axios上,達到支持請求別名的目的
axios.creat = axiosInstance.creat.bind(axiosInstance);
axios.get = axiosInstance.get.bind(axiosInstance);
axios.post = axiosInstance.post.bind(axiosInstance);
axios.cache = axiosInstance.cache.bind(axiosInstance);
axios.cache.storage = axiosInstance.storage.bind(axiosInstance);

Axios class

初始化

  • defaultConfig 默認配置,即 defaults.js
  • axios.creat 用戶配置覆蓋默認配置
  • 注意配置初始化後 mergeConfig 不能被污染,config 需通過參數傳遞
constructor(config = defaults) {
    this.defaultConfig = config;
  }
creat(_config = {}) {
  this.defaultConfig = mergeConfig(this.defaultConfig, _config);
}

請求別名

  • axios 兼容 axios(config) 或 axios(url[, config]);
  • 別名都只是 config 合併,最終都通過 axios.requst()發起請求;
  axios($1 = {}, $2 = {}) {
    let config = $1;
    // 兼容axios(url[, config])方式
    if (typeof $1 === 'string') {
      config = $2;
      config.url = $1;
    }
    return this.request(config);
  }

  post(url, data = {}, _config = {}) {
    const config = {
      ..._config,
      url,
      data,
      method: 'POST',
    };
    return this.request(config);
  }

請求方法 _request

請求配置預處理

  • 實現 baseUrl
  • 實現 transformRequest(轉換請求數據)
  _request(_config = {}) {
    let config = mergeConfig(this.defaultConfig, _config);
    const { baseUrl, url, header, data = {}, transformRequest } = config;
    const computedConfig = {
      header: {
        'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
        ...header,
      },
      ...(baseUrl && {
        url: combineUrl(url, baseUrl),
      }),
      ...(transformRequest &&
        typeof transformRequest === 'function' && {
          data: transformRequest(data),
        }),
    };
    config = mergeConfig(config, computedConfig);
    return wxRequest(config);
  }

wx.request

發起請求、處理響應

  • 實現 validateStatus(狀態碼映射 resolve)
  • 實現 transformResponse(轉換響應數據)
  • 實現 resolveWrap、rejectWrap(響應狀態處理)
export default function wxRequest(config) {
  return new Promise((resolve, reject) => {
    wx.request({
      ...config,
      success(res) {
        const {
          resolveWrap,
          rejectWrap,
          transformResponse,
          validateStatus,
        } = config;
        if ((validateStatus && validateStatus(res)) || ifSuccess(res)) {
          const _resolve = resolveWrap ? resolveWrap(res) : res;
          return resolve(
            transformResponse ? transformResponse(_resolve) : _resolve
          );
        }
        return reject(rejectWrap ? rejectWrap(res) : res);
      },
      fail(res) {
        const { rejectWrap } = config;
        reject(rejectWrap ? rejectWrap(res) : res);
      },
    });
  });
}

請求緩存的實現

  • 默認使用內存緩存,可配置使用 localStorage
  • 封裝了 Storage 與 Buffer 類,與 Map 接口一致:get/set/delete
  • 支持緩存標記&過期時間
  • 緩存唯一 key 值,默認使用 url&data 生成,無需指定
  import Buffer from '../utils/cache/Buffer';
  import Storage from '../utils/cache/Storage';
  import StorageMap from '../utils/cache/StorageMap';


  /**
   * 請求緩存api,緩存於本地緩存中
   */
  storage(url, data = {}, _config = {}) {
    const config = {
      ..._config,
      url,
      data,
      method: 'POST',
      cacheStorage: true,
    };
    return this._cache(config);
  }

  /**
   * 請求緩存
   * @param {Object} _config 配置
   * @param {Boolean} _config.useCache 是否開啟緩存
   * @param {String} _config.cacheName 緩存唯一key值,默認使用url&data生成
   * @param {Boolean} _config.cacheStorage 是否開啟本地緩存
   * @param {Any} _config.cacheLabel 緩存標誌,請求前會對比該標誌是否變化來決定是否使用緩存,可用useCache替代
   * @param {Number} _config.cacheExpireTime 緩存時長,計算緩存過期時間,單位-秒
   */
  _cache(_config) {
    const {
      url = '',
      data = {},
      useCache = true,
      cacheName: _cacheName,
      cacheStorage,
      cacheLabel,
      cacheExpireTime,
    } = _config;
    const computedCacheName = _cacheName || `${url}#${JSON.stringify(data)}`;
    const cacheName = StorageMap.getCacheName(computedCacheName);

    // return buffer
    if (useCache && Buffer.has(cacheName, cacheLabel)) {
      return Buffer.get(cacheName);
    }

    // return storage
    if (useCache && cacheStorage) {
      if (Storage.has(cacheName, cacheLabel)) {
        const data = Storage.get(cacheName);
        // storage => buffer
        Buffer.set(
          cacheName,
          Promise.resolve(data),
          cacheExpireTime,
          cacheLabel
        );
        return Promise.resolve(data);
      }
    }
    const curPromise = new Promise((resolve, reject) => {
      const handleFunc = (res) => {
        // do storage
        if (useCache && cacheStorage) {
          Storage.set(cacheName, res, cacheExpireTime, cacheLabel);
        }
        return res;
      };

      this._request(_config)
        .then((res) => {
          resolve(handleFunc(res));
        })
        .catch(reject);
    });

    // do buffer
    Buffer.set(cacheName, curPromise, cacheExpireTime, cacheLabel);

    return curPromise;
  }