1.2.0 • Published 2 years ago

@lxjx/request v1.2.0

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

📑Introduce

一个用来简化 XHR 请求的库

✨Features

  • 几乎支持所有 javascript 运行时, 可以和任何请求库(fetch/axios/小程序等)搭配使用
  • 集中式的错误、操作反馈
  • 全局 loading、token 等
  • 请求缓存
  • 插件化,可以通过插件来获取更多的能力

📦Installation

yarn add @lxjx/request
// or
npm install @lxjx/request

使用

浏览器环境

这里以axios为例

💡 如果使用 js,忽略下面的所有类型声明

import createInstance from '../src';
import axios, { AxiosRequestConfig } from 'axios';

// 通过传入AxiosRequestConfig来指定request(options)中options的类型
const request = createInstance<AxiosRequestConfig>({
  /* ############## 适配器配置 ############## */

  // 启用axios
  // - 直接传入是因为axios(option)接口支持, 也可以写成 `fetchAdapter: opt => axios(opt)`
  // - 其他的如fetch为 `fetchAdapter: ({ url, ...opt }) => fetch(url, opt)`。 
  // - 💡fetch配置是默认的,如果通过fetch进行请求,可以跳过适配器配置
  fetchAdapter: axios,

  /* ############## 其他配置: 拦截器、加载状态、消息反馈、根据服务器返回进行的个性化配置等 ##############  */

  // 在http状态码正常时,根据返回值检测该次请求是否成功
  checkStatus(data: any) {
    return data && data.code === 0;
  },
  // 用来从服务端请求中提取提示文本的字段
  messageField: 'message',
  // 配置正确或错误的反馈方式
  feedBack(message: string, status: boolean) {
    console.log('请求提示:', status ? '成功' : '失败');
    console.log('反馈消息:', message);
  },
  // 将response预格式化为自己想要的格式后返回
  format: response => response?.data?.data,
  // 请求开始,可以在此配置loading,token等
  start(extraOption, requestConfig) {
    console.log('请求开始');

    requestConfig.headers = {
      ...requestConfig.headers,
      token: 'a token',
    };

    extraOption.loading && console.log('请求中...');

    return Math.random(); // 返回值作为finish的第三个参数传入,用于关闭弹窗等
  },
  // 请求结束,在此关闭loading或执行其它清理操作, flag是start()中返回的值
  finish(extraOption, requestConfig, flag?: any) {
    console.log('请求结束', flag);
  },
});

interface ResponseType {
  name: string;
  age: number;
}

// 通过request发起请求,ResponseType是返回值的类型,默认为any
request<ResponseType>('/api/user', {
  // 正常的axios配置
  method: 'POST',
  timeout: 8000,
  // 独立于axios的额外配置,用于增强请求行为
  extraOption: {
    useServeFeedBack: true,
    loading: true,
    // 一些基础配置在请求时也可以进行配置,权重大于createInstance时配置的
    start() {},
  },
}).then(([err, res]) => {
  console.log('-----请求完成-----');
  console.log('err:', err);
  console.log('res:', res);

  // 当err存在时表示该次请求包含错误
  if (err || !res) return;

  // 在这里执行请求成功后的操作
});

// 如果不喜欢错误优先风格的请求方式,也可以使用promise版本
request
  .promise<ResponseType>('/api/user', {
    method: 'POST',
    extraOption: {
      loading: true,
    },
  })
  .then(res => {
    console.log(res);
  })
  .catch(err => {
    console.log(err);
  });

其他环境

在其他客户端宿主环境使用(ReactNative/小程序等)时,除了适配器配置外,还需额外配置缓存方式,如果不需要缓存功能可以跳过。

以微信小程序为例:

const request = createInstance({
  fetchAdapter(options) {
    return new Promise((res, rej) => {
      wx.request({
        ...options,
        success(response) {
          res(response);
        },
        fail(error) {
          rej(error);
        },
      });
    });
  },
  // 如果需要缓存,添加以下配置 (由于小程序端不支持sessionStorage,不推荐进行缓存), 不要使用异步版本的存储方法
  setStorageAdapter(key, val) {
    wx.setStorageSync(key, val);
  },
  getStorageAdapter(key) {
    return wx.getStorageSync(key);
  },
  removeStorageAdapter(key) {
    wx.removeStorageSync(key);
  },
});

使用插件

request 内部所有配置项、缓存等的功能都是由插件实现的,插件接口也对外提供,可以藉此进行功能扩展。

插件为Plugin 类的子类,你可以通过重写不同的钩子来为插件实现不同的能力

Plugin 类:

class Plugin<OPTIONS extends BaseRequestOptions, ExtraExpand = any> {
  constructor(
          public ctx: any, // 在不同插件间共享数据的对象
          public createOptions: CreateOptions<OPTIONS, ExtraExpand>, // 创建时配置
          public options: MixOpt<OPTIONS, ExtraExpand>, // request中传入的配置
          public extraOptions: ExtraOptions<ExtraExpand> // 等于options.extraOptions // eslint-disable-next-line no-empty-function // eslint-disable-next-line no-empty-function
  ) {}

  /**
   * 帮助函数,从extraOptions或createOptions中取出指定名称的方法,前者优先级更高
   * */
  getCurrentOption<key extends keyof Options<OPTIONS, ExtraExpand>>(
          optionField: key
  ): Options<OPTIONS, ExtraExpand>[key] {
    return this.extraOptions[optionField] || this.createOptions[optionField];
  }

  /**
   * 请求开始之前
   * * 为此钩子返回一个Promise,可以阻断本次请求并以返回值作为request的返回
   * * 只要有任意一个before返回了值,其他钩子的before将不再执行
   * * 为了保证接口签名一致,最好返回与request返回一致且resolve的Promise对象
   * @example
   before() {
        return Promise.resolve([null, { tip: '这是一段直接从本地拉取的数据' }] as const);
      }
   * */
  before?(): Promise<readonly [null, any]> | void;

  /**
   * 转换请求结果并返回
   * @param response - response是根据你配置的请求库类型返回决定的
   * @return - 必须将经过处理后的response return,其他插件才能接受到经过处理后的response
   *
   * * 在转换过程中可以通过抛出错误来使该次请求'失败', 并进入catch
   * */
  pipe?(response: Response): Response;

  /**
   * 请求成功,对数据的处理请在pipe中执行,此函数只应用于进行消息反馈等
   * @param response - response是根据你配置的请求库类型决定的
   * */
  success?(response: any): void;

  /** 请求失败 */
  error?(error: RequestError): void;

  /** 请求结束 */
  finish?(): void;
}

log 插件为例, 用来 log 每一个生命周期:

import { Plugin } from '@lxjx/request';

class Log extends Plugin {
  before() {
    console.log('请求开始啦');
  }

  pipe(response) {
    console.log('接收到response并正在进行处理');
    return response; // 务必原样返回,否则其他插件会接收不到response
  }

  success() {
    console.log('请求成功');
  }

  error() {
    console.log('请求失败');
  }

  finish() {
    console.log('请求结束');
  }
}

createInstance()中使用

import createInstance from '@lxjx/request';

const request = createInstance({
  plugins: [Log], // 在request进行的每个生命周期进行log
  // ...其他配置
});

API

💡 为了方便阅读,所有类型签名都是简化过的,可以在 ide 中查看更详细的签名信息。

createInstance()

创建一个Request实例

/**
 * 创建Request实例
 * <OPTIONS> - 创建的request函数的配置参数类型
 * <ExtraExpand> - 如果指定,会用于扩展extraOption的类型, 当你想要自定义额外的配置时使用(如extraOption.token)
 * @param options - 配置
 * @return - Request实例
 * */
interface CreateInstance {
  <OPTIONS, ExtraExpand>(options: CreateOptions): Request;
}

options

createInstance() 接收的配置

// 特有配置
interface CreateOptions {
  /**
   * 请求适配器, 可以是任意接收配置并返回promise的函数
   * * 配置遵循BaseRequestOptions, 如果使用的请求库不符合这些字段名配置,可以通过此方法抹平
   * * 对于大多数请求库(fetch/axios),只需要简单的透传options即可
   * */
  fetchAdapter?: (options: OPTIONS) => Promise<any>;
  /** 自定义缓存的获取方式,默认取全局下的localStorage.setItem (如果存在) */
  setStorageAdapter?: (key: string, val: any) => void;
  /** 自定义缓存的取值方式,默认取全局下的localStorage.getItem (如果存在) */
  getStorageAdapter?: (key: string) => any;
  /** 自定义缓存的清理方式 */
  removeStorageAdapter?: (key: string) => void;
  /** 传递给Request的默认配置,会在请求时深合并到请求配置中 */
  baseOptions?: Partial<OPTIONS>;
  /** 插件 */
  plugins?: Array<typeof Plugin>;
}

// 基础配置,支持在createInstance()和request()中配置,request()中的配置优先级大于前者
interface Options {
  /** 接收服务器response,需要返回一个boolean值用于验证该次请求是否成功(只需要验证服务端的返回,状态码、超时等错误已自动处理) */
  checkStatus?(data: any): boolean;
  /** 用来从服务端请求中提取提示文本的字段 */
  messageField?: string;
  /** 配置反馈方式, 在此处通过Modal、Toast等提示库进行反馈提示 */
  feedBack?(
    message: string,
    status: boolean,
    extraOption: ExtraOptions,
    requestConfig: OPTIONS,
  ): void;
  /** 将response格式化为自己想要的格式后返回, 会在所有插件执行完毕后执行, 返回值为request接收的最终值  */
  format?(response: any, extraOption: ExtraOptions, requestConfig: OPTIONS): any;
  /** 请求开始, 可以进行show loading、添加headers头等操作 */
  start?(extraOption: ExtraOptions, requestConfig: MixOpt<OPTIONS>): any;
  /**
   * 请求结束
   * * flag是start方法的返回值, 某些loading库会返回一个关闭表示,可以由此传递
   * */
  finish?(extraOption: ExtraOptions, requestConfig: OPTIONS, flag?: any): void;
}

request()

/**
 * 请求方法, 返回一个必定resolve 元组[Error, Data]的Promise, 如果Error不为null则表示请求异常
 * 错误分为两种:
 *  1. 常规错误。跨域,网络错误、请求链接等错误,由fetchAdapter配置的请求库提供
 *  2. 服务器返回错误。状态码异常、checkStatus未通过等,此时Error对象会包含一个response属性,为服务器返回数据
 * */
export interface Request {
  <Data = any>(url: string, options?: OPTIONS): Promise<
    readonly [Error | RequestError | null, Data | null]
  >;
}

/**
 * request配置必须遵循的一些字段名
 * 一些配置字段需要在内部使用,所以通过此接口对配置进行简单约束
 * 例如,请求体必须以data或body字段传递,地址必须由url指定。
 * */
export interface BaseRequestOptions {
  /** 请求url */
  url?: string;
  /** 请求体, 该字段可以是data或body */
  data?: any;
  /** 请求体, 与data相同,用于在使用fetch时减少配置 */
  body?: any;
  /** 请求参数  */
  params?: any;
  /** 请求头 */
  headers?: any;
}

options

request() 的配置为创建实例时通过泛型指定的类型 + 一些内置的额外配置

example:

requset('/user', {
  methods: 'POST', // 这里是传递给请求器的配置
  extraOption: {
    // 这里是内部提供的额外配置
    useServeFeedBack: true,
    loading: '请求中...',
  },
});
interface ExtraOptions extends Options<any> {
  /**
   * 设置缓存时间,值为true时缓存30s,为数字时表示指定的秒数
   * ⛔ 不要对包含FormData或content-type不是application/json这类的的请求开启缓存
   * ✅ 需要缓存的一般都是查询类接口
   * */
  cache?: boolean | number;
  /** 为true时即使返回服务器状态码正确依然会以服务器返回的消息(根据serverMsgField配置项确定)作为反馈提示 */
  useServeFeedBack?: boolean;
  /** 静默模式,无论正确与否不会有任何提示 */
  quiet?: boolean;
  /** 默认会返回经过format处理的结果,为true时返回原始的response */
  plain?: boolean;
  /** 是否显示加载,需要在start/finish中接收并进行配置 */
  loading?: boolean | string;
  /** 自定义请求成功的提示, 启用此项时,不需要再配置useServeFeedBack。 会覆盖其他根据配置生成的提示消息 */
  successMessage?: string;
  /** 用于传递其他额外配置时,如 hasToken */
  [key: string]: any;
}

// 基础配置,支持在createInstance()和request()中配置,request()中的配置优先级大于前者
interface Options {
  /** 接收服务器response,需要返回一个boolean值用于验证该次请求是否成功(只需要验证服务端的返回,状态码、超时等错误已自动处理) */
  checkStatus?(data: any): boolean;
  /** 用来从服务端请求中提取提示文本的字段 */
  messageField?: string;
  /** 配置反馈方式, 在此处通过Modal、Toast等提示库进行反馈提示 */
  feedBack?(
    message: string,
    status: boolean,
    extraOption: ExtraOptions,
    requestConfig: OPTIONS,
  ): void;
  /** 将response格式化为自己想要的格式后返回, 会在所有插件执行完毕后执行, 返回值为request接收的最终值  */
  format?(response: any, extraOption: ExtraOptions, requestConfig: OPTIONS): any;
  /** 请求开始, 可以进行show loading、添加headers头等操作 */
  start?(extraOption: ExtraOptions, requestConfig: MixOpt<OPTIONS>): any;
  /**
   * 请求结束
   * * flag是start方法的返回值, 某些loading库会返回一个关闭表示,可以由此传递
   * */
  finish?(extraOption: ExtraOptions, requestConfig: OPTIONS, flag?: any): void;
}
1.2.0

2 years ago

1.1.1

3 years ago

1.1.2

3 years ago

1.1.0

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago

0.22.2

4 years ago

0.22.1

4 years ago

0.22.0

4 years ago

0.20.11

4 years ago

0.20.10

4 years ago

0.20.9

4 years ago

0.20.8

4 years ago

0.20.7

4 years ago

0.20.6

4 years ago

0.20.5

4 years ago

0.20.4

5 years ago

0.20.3

5 years ago

0.20.2

5 years ago

0.20.1

5 years ago

0.20.0

5 years ago

0.0.4

5 years ago

0.0.3

5 years ago

0.0.2

5 years ago

0.0.1

5 years ago