Skip to content
On this page

[DEPRECATED] uni-app-network

该依赖已废弃,请迁移到功能一致的 @uni-helper/uni-network

License

npm

uni-app 打造的基于 Promise 的 HTTP 客户端。灵感和代码绝大部分源于 axios@0.27.2

起步

介绍

uni-app-network 是什么?

uni-app-network 是一个为 uni-app 打造的 基于 Promise 的 HTTP 客户端。灵感和代码绝大部分源于 axios@0.27.2

特性

设备和浏览器支持

需要设备和浏览器兼容 ECMAScript 5 并支持 Promise。你可以自行补充 polyfill。

安装

使用 npm

shell
npm install uni-app-network
1

使用 yarn

shell
yarn install uni-app-network
1

使用 pnpm

shell
pnpm install uni-app-network
1

使用 cnpm

shell
cnpm install uni-app-network
1

不考虑支持 uni_modules

例子

发起一个 GET 请求

typescript
import uan from 'uni-app-network';

uan
  .get('user?ID=12345')
  .then((response) => {
    console.log('response', response);
  })
  .catch((error) => {
    console.log('error', error);
  })
  .finally(() => {});

// 上述请求和以下等同
uan
  .get('/user', {
    params: {
      ID: 12345,
    },
  })
  .then((response) => {
    console.log('response', response);
  })
  .catch((error) => {
    console.log('error', error);
  })
  .finally(() => {});

// 支持 async/await
async function getUser() {
  try {
    const response = await uan.get('/user?ID=12345');
    console.log(response);
  } catch (error) {
    console.error(error);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

发起一个 POST 请求

typescript
uan
  .post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone',
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  })
  .finally(() => {});
1
2
3
4
5
6
7
8
9
10
11
12

并发请求

typescript
function getUserAccount() {
  return uan.get('/user/12345');
}

function getUserPermissions() {
  return uan.get('/user/12345/permissions');
}

Promise.all([getUserAccount(), getUserPermissions()]).then((responses) => {
  const acct = responses[0];
  const perm = responses[1];
});
1
2
3
4
5
6
7
8
9
10
11
12

API

创建请求

可以向 uan 传递相关配置来创建请求。

uan(config)

typescript
uan({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone',
  },
});
1
2
3
4
5
6
7
8

uan(url[, config])

typescript
uan('/user/12345');
1

为了方便起见,已经为所有支持的请求方法提供了别名。在使用别名方法时,urlmethoddata 不需要在配置中指定。

  • uan.request(config)
  • uan.download(config)
  • uan.upload(config)
  • uan.get(url[, config])
  • uan.delete(url[, config])
  • uan.head(url[, config])
  • uan.options(url[, config])
  • uan.trace(url[, config])
  • uan.connect(url[, config])
  • uan.post(url[, data[, config]])
  • uan.put(url[, data[, config]])
  • uan.patch(url[, data[, config]])

实例

创建实例

可以使用自定义配置创建一个实例。

uan.create([config])

typescript
const instance = uan.create({
  baseUrl: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: { 'X-Custom-Header': 'foobar' },
});
1
2
3
4
5

实例方法

以下是可用的实例方法。指定的配置将与实例的配置合并。

  • uan.request(config)
  • uan.download(config)
  • uan.upload(config)
  • uan.get(url[, config])
  • uan.delete(url[, config])
  • uan.head(url[, config])
  • uan.options(url[, config])
  • uan.trace(url[, config]])
  • uan.connect(url[, config]])
  • uan.post(url[, data[, config]])
  • uan.put(url[, data[, config]])
  • uan.patch(url[, data[, config]])
  • uan.getUri([config])

请求配置

这些是创建请求时可以用的配置选项。只有 url 是必需的。如果没有指定 method 且没有指定 adapter,请求将默认使用 GET 方法。

typescript
{
  // `url` 是用于请求的服务器 URL
  url: '/user',

  // `baseUrl` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL
  // 设置一个 `baseUrl` 便于为 uan 实例的方法传递相对 URL
  baseUrl: 'https://some-domain.com/api/',

  // 自定义请求头
  headers: {
    'content-type': 'application/json',
  },

  // `params` 是与请求一起发送的 URL 参数
  params: {
    ID: 12345
  },

  // `paramsSerializer` 是可选方法,主要用于序列化 `params`
  // 默认使用 [qs](https://github.com/ljharb/qs) 序列化
  paramsSerializer: (params) => { /* 返回一个字符串 */ }

  // `timeout` 指定请求超时的毫秒数
  // 如果请求时间超过 `timeout` 的值,则请求会被中断
  timeout: 1000, // 默认值是 `0` (永不超时)

  // `adapter` 允许自定义处理请求
  // 可以指定为 'request'、`upload` 和 `download` 三者之一
  // 也可以指定为一个方法,返回一个 Promise 并提供一个有效的响应
  adapter: 'request' // 默认值
  adapter: (config) => { /* ... */ },

  // `validateStatus` 定义了对于给定的 HTTP 状态码是 resolve 还是 reject
  // 如果 `validateStatus` 返回 `true`、`null` 或 `undefined`
  // 则 promise 将会被 resolve,否则会被 reject
  validateStatus: function (status) {
    return status >= 200 && status < 300; // 默认值
  },

  // 用于取消请求
  signal: new AbortController().signal,

  // 用于取消请求
  cancelToken: new CancelToken(function (cancel) {
  }),

  // 监听 HTTP Response Header 事件
  // 会比请求完成事件更早
  onHeadersReceived: (result) => { /* ... */ },

  // request 使用
  // 创建请求时使用的方法
  method: 'GET', // 默认值

  // request 使用
  // `data` 是作为请求体被发送的数据
  // 必须是以下类型之一:string、ArrayBuffer、Record<string, any>
  data: {
    firstName: 'Fred'
  },
  data: 'Country=Brasil&City=Belo Horizonte',

  // request 使用
  // 返回的数据类型
  // 如果设置为 json,会尝试对返回的数据做一次 JSON.parse
  dataType: 'json', // 默认值

  // request 使用
  // 响应的数据类型,选项包括 'text' 和 'arraybuffer'
  responseType: 'text', // 默认值

  // request 使用
  // 是否开启 http2
  enableHttp2: false, // 默认值

  // request 使用
  // 是否开启 quic
  enableQuic: false, // 默认值

  // request 使用
  // 是否开启缓存
  enableCache: false, // 默认值

  // request 使用
  // 是否开启 HttpDNS 服务
  enableHttpDNS: false, // 默认值

  // request 使用
  // HttpDNS 服务商 Id
  httpDNSServiceId: '',

  // request 使用
  // 是否开启 transfer-encoding chunked
  enableChunked: false, // 默认值

  // request 使用
  // 是否在 wifi 下使用移动网络发送请求
  forceCellularNetwork: false, // 默认值

  // request 使用
  // 是否验证 ssl 证书
  sslVerify: true, // 默认值

  // request 使用
  // 跨域请求时是否需要使用凭证
  withCredentials: false, // 默认值

  // request 使用
  // 是否在 DNS 解析时优先使用 ipv4
  firstIpv4: false, // 默认值

  // request 使用
  // 监听 Transfer-Encoding Chunk Received 事件
  // 当接收到新的 chunk 时触发
  onChunkReceived?: (response) => {/* ... */};

  // upload 使用
  // 需要上传的文件列表,files 和 filePath 必填一个
  // 使用该参数时,filePath 和 name 无效
  // 不支持小程序
  files: [],

  // upload 使用
  // 文件类型
  fileType: 'image', // image, video, audio

  // upload 使用
  // 文件对象
  file: new File(),

  // upload 使用
  // 文件路径,files 和 filePath 必填一个
  //
  // download 使用
  // 文件下载后存储的本地路径
  filePath: '/fake/path',

  // upload 使用
  // 文件名称
  name: 'fake-file.png',

  // upload 使用
  // 一个对象,会作为 HTTP 请求中其它额外的 form data
  formData?: Record<string, any>,

  // download 使用
  // 下载进度变化时触发
  // 优先级 onDownloadProgress > onDownloadProgressUpdate > onProgress > onProgressUpdate
  onDownloadProgress?: UanOnProgress;

  // download 使用
  // 下载进度变化时触发
  // 优先级 onDownloadProgress > onDownloadProgressUpdate > onProgress > onProgressUpdate
  onDownloadProgressUpdate?: UanOnProgress;

  // upload 使用
  // 上传进度变化时触发
  // 优先级 onUploadProgress > onUploadProgressUpdate > onProgress > onProgressUpdate
  onUploadProgress?: UanOnProgress;

  // upload 使用
  // 上传进度变化时触发
  // 优先级 onUploadProgress > onUploadProgressUpdate > onProgress > onProgressUpdate
  onUploadProgressUpdate?: UanOnProgress;

  // upload / download 使用
  // 上传/下载进度变化时触发
  // 优先级 onUploadProgress / onDownloadProgress > onUploadProgressUpdate / onDownloadProgressUpdate > onProgress > onProgressUpdate
  onProgress?: UanOnProgress;

  // upload / download 使用
  // 上传/下载进度变化时触发
  // 优先级 onUploadProgress / onDownloadProgress > onUploadProgressUpdate / onDownloadProgressUpdate > onProgress > onProgressUpdate
  onProgressUpdate?: UanOnProgress;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

响应结构

一个请求的响应包含以下信息。

typescript
{
  // `errMsg` 是可选的错误信息
  errMsg: '',

  // `errno` 是可选的错误代码
  errno: 0,

  // `profile` 是可选的调试信息
  profile: {},

  // `config` 是 `uan` 请求的配置信息
  config: {},

  // `task` 是对应的 task 信息
  task: {}

  // `status` 来自服务器响应的 HTTP 状态码
  status: 200,

  // `statusText` 来自服务器响应的 HTTP 状态信息
  statusText: 'OK',

  // `headers` 是服务器响应头
  // 所有的 header 名称都是小写,而且可以使用方括号语法访问
  // 例如: `response.headers['content-type']`
  headers: {},

  // `data` 是由服务器提供的响应数据
  data: {},

  // request 特有
  // 服务器提供的 cookies 数据
  cookies: [],

  // download 特有
  // 临时本地文件路径
  // 没传入 filePath 指定文件存储路径时会返回,下载后的文件会存储到一个临时文件
  tempFilePath: '',

  // download 特有
  // 用户本地文件路径
  // 传入 filePath 时会返回,跟传入的 filePath 一致
  filePath: '',
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

当使用 then 时,你将接收如下响应:

typescript
uan.get('/user/12345').then((response) => {
  console.log(response.data);
  console.log(response.status);
  console.log(response.statusText);
  console.log(response.headers);
  console.log(response.config);
});
1
2
3
4
5
6
7

当使用 catch,或者传递一个 rejection callback 作为 then 的第二个参数时,响应可以作为 error 对象被使用,正如在 错误处理 部分解释的那样。

默认配置

你可以指定默认配置,它将作用于每个请求。

全局配置默认值

typescript
uan.defaults.baseUrl = 'https://api.example.com';
1

自定义实例默认值

typescript
// 创建实例时配置默认值
const instance = uan.create({
  baseUrl: 'https://api.example.com',
});

// 创建实例后修改默认值
instance.defaults.baseUrl = 'https://api.another-example.com';
1
2
3
4
5
6
7

配置的优先级

配置将会按优先级进行合并。优先级从低到高是内置的默认值、实例的 defaults 配置、请求的 config。后面的优先级要高于前面的。下面有一个例子。

typescript
// 使用库提供的默认配置创建实例
// 此时超时配置的默认值是 `0`
const instance = uan.create();

// 重写库的超时默认值
// 现在,所有使用此实例的请求都将等待 2.5 秒,然后才会超时
instance.defaults.timeout = 2500;

// 重写此请求的超时时间,因为该请求需要很长时间
instance.get('/longRequest', {
  timeout: 5000,
});
1
2
3
4
5
6
7
8
9
10
11
12

拦截器

在请求或响应被 thencatch 处理前拦截它们。

添加拦截器

typescript
// 添加请求拦截器
uan.interceptors.request.use(
  function (config) {
    // 在发送请求之前做些什么
    return config;
  },
  function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  },
);

// 添加响应拦截器
uan.interceptors.response.use(
  function (response) {
    // 2xx 范围内的状态码都会触发该函数
    // 对响应数据做点什么
    return response;
  },
  function (error) {
    // 超出 2xx 范围的状态码都会触发该函数
    // 对响应错误做点什么
    return Promise.reject(error);
  },
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

可以给自定义实例添加拦截器。

typescript
const instance = uan.create();
instance.interceptors.request.use(() => {
  /*...*/
});
1
2
3
4

移除拦截器

typescript
const myInterceptor = uan.interceptors.request.use(() => {
  /*...*/
});
uan.interceptors.request.eject(myInterceptor);
1
2
3
4

也可以移除所有请求或响应的拦截器。

typescript
const instance = uan.create();
instance.interceptors.request.use(() => {
  /*...*/
});
instance.interceptors.request.clear(); // 移除所有请求拦截器
instance.interceptors.response.use(() => {
  /*...*/
});
instance.interceptors.response.clear(); // 移除所有响应拦截器
1
2
3
4
5
6
7
8
9

拦截器选项

当你添加请求拦截器时,uni-app-network 默认认为它们是异步的。当主线程被阻塞时,这可能会导致 uni-app-network 请求的执行延迟(底层为拦截器创建了一个 Promise,你的请求被放在了调用栈的底部)。如果你的请求拦截器是同步的,你可以在选项对象中添加一个标志,告诉 uni-app-network 同步运行代码,避免请求执行中的任何延迟。

typescript
uan.interceptors.request.use(
  (config) => {
    config.headers.test = 'I am only a header!';
    return config;
  },
  null,
  { synchronous: true },
);
1
2
3
4
5
6
7
8

如果你想根据运行时检查来执行某个拦截器,你可以在 options 对象中设置 runWhen 函数。当且仅当 runWhen 的返回值为 false 时,拦截器不会被执行。该函数将和 config 对象一起被调用(别忘了,你也可以绑定你自己的参数)。当你有一个只需要在特定时间运行的异步请求拦截器时,这可能会很方便。

typescript
const onGetCall = (config) => config.method.toUpperCase() === 'GET';
uan.interceptors.request.use(
  (config) => {
    config.headers.test = 'special get headers';
    return config;
  },
  null,
  { runWhen: onGetCall },
);
1
2
3
4
5
6
7
8
9

多个拦截器

假设你添加了多个响应拦截器,并且响应是 fulfilled 状态时:

  • 执行每个拦截器
  • 按照添加的顺序执行它们
  • 只返回最后一个拦截器的结果
  • 每个拦截器都会收到其前一个拦截器的结果
  • fulfilled 拦截器抛出时
    • 后面的 fulfilled 拦截器不会被调用
    • 后面的 rejection 拦截器会被调用
    • 一旦被捕获,后面的另一个 fulfilled 拦截器会被再次调用(就像在一个 Promise 链中一样)

错误处理

typescript
uan.get('/user/12345').catch((error) => {
  if (error.response) {
    // 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
    console.log(error.response.data);
    console.log(error.response.status);
    console.log(error.response.headers);
  } else if (error.task) {
    // 请求已经成功发起,但没有收到响应
    // `error.task` 是 task 实例
    console.log(error.task);
  } else {
    // 发送请求时出了点问题
    console.log('Error', error.message);
  }
  console.log(error.config);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

使用 validateStatus 配置选项,可以自定义抛出错误的 HTTP code。

typescript
uan.get('/user/12345', {
  validateStatus: (status) => {
    return status < 500; // 处理状态码小于 500 的情况
  },
});
1
2
3
4
5

如果你追求语义化,可以使用导出的状态码、statuseshttp-status-codesnode-http-status

typescript
import { HttpStatusCode } from 'uni-app-network';

uan.get('/user/12345', {
  validateStatus: (status) => {
    return status < HttpStatusCode.InternalServerError; // 处理状态码小于 500 的情况
  },
});
1
2
3
4
5
6
7

使用 toJSON 可以获取更多关于 HTTP 错误的信息。

typescript
uan.get('/user/12345').catch((error) => {
  console.log(error.toJSON());
});
1
2
3

如果需要针对 UanError 和非 UanError 做处理,可以使用导出的 isUanError 方法判断。

typescript
import { isUanError } from 'uni-app-network';

uan.get('/user/12345').catch((error) => {
  if (isUanError(error)) {
    /* ... */
  } else {
    /* ... */
  }
});
1
2
3
4
5
6
7
8
9

取消请求

支持使用 AbortController 取消请求。在 uni-app 环境,你可能需要使用 polyfill

typescript
const controller = new AbortController();

uan
  .get('/foo/bar', {
    signal: controller.signal,
  })
  .then(function (response) {
    //...
  });
// 取消请求
controller.abort();
1
2
3
4
5
6
7
8
9
10
11

你也可以使用 CancelToken

typescript
const CancelToken = uan.CancelToken;
const source = CancelToken.source();

uan
  .get('/user/12345', {
    cancelToken: source.token,
  })
  .catch(function (thrown) {
    if (uan.isUanCancel(thrown)) {
      console.log('Request canceled', thrown.message);
    } else {
      // 处理错误
    }
  });

uan.post(
  '/user/12345',
  {
    name: 'new name',
  },
  {
    cancelToken: source.token,
  },
);
// 取消请求(信息是可选的)
source.cancel('Operation canceled by the user.');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

你也可以通过向 CancelToken 构造函数传递一个执行函数来创建一个 CancelToken 实例。

js
const CancelToken = uan.CancelToken;
let cancel;

uan.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  }),
});

// 取消请求
cancel();
1
2
3
4
5
6
7
8
9
10
11

注意:你可以用同一个 CancelToken / AbortController 取消几个请求。 如果在发起请求的时候已经取消请求,那么该请求就会被立即取消,不会真正发起请求。

其它

构建

目前 uni-app-network 会使用 tsupuni API 之外的部分转译到 esnextuni API 需要在项目构建时由 uni-app 官方提供的插件处理。

对于 vue-cli,请修改项目根目录 vue.config.js 如下所示。

js
module.exports = {
  transpileDependencies: ['uni-app-network'],
};
1
2
3

对于 vite,你无需手动额外调整。

高级功能

对于缓存、去重的高级功能,建议结合 @tanstack/vue-queryswrvvue-request 等库使用。

为什么不是 axios

axios 非常棒,但它面对的是浏览器和 Node.js,即使使用了 adapter,某些底层功能也可能会在小程序内报错,而且需要修改 axios 大部分类型定义。

比较

如果你发现这里信息已经过时,请提交 ISSUE 或 PR。

axiosluch-requestuni-ajaxuni-app-network
基本信息npm npmnpm npmnpm npmnpm npm
开发语言JavaScriptJavaScriptJavaScriptTypeScript
类型支持index.d.ts(没有考虑 uni-appindex.d.ts(泛型支持较差)index.d.ts包含
运行环境浏览器和 Node.jsuni-appuni-appuni-app
Promise
uni_modules××
npm
实例化
请求说明
响应说明×
任务说明×(没有考虑 uni-app  任务)×√(只有 requestTask  说明)√(只有简单说明)
适配器√(内置 xhrhttp×
uni.request×(自行开发,还需要覆写类型)
uni.downloadFile×(自行开发,还需要覆写类型)×(自行开发,还需要覆写类型)
uni.uploadFile×(自行开发,还需要覆写类型)×(自行开发,还需要覆写类型)
请求拦截器
响应拦截器
配置说明
取消请求说明×
错误处理说明×
测试完善部分部分
使用示例

资源

致谢

Released under the MIT License.