大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
高级前端进阶
今天给大家带来的主题是undici,即Nodejs的fetch的底层实现。话不多说,直接开始!
1.什么是 undici
Undici 发音为/undiʧi]/,是一个快速、可靠且符合规范的 HTTP/1.1 客户端,它是从头开始编写的,代表了 Node.js HTTP 堆栈的演变。 它正迅速成为最受欢迎的软件包之一,目前 Undici 在 Github 上有超过 4.6K 的 star、每周在 npm 上的平均下载量接近 2795K 次,有超过 202K 的项目使用它。
Undici 在意大利语中是十一的意思, 1.1 -> 11 -> 十一 -> Undici,它也是《怪奇物语》的参考资料。
在 Node.js V18 中,默认提供了一个全局 fetch,该 fetch 的底层实现就是来自 undici。
2.快速使用 undici
首先需要使用 npm 安装 undici:
npm install undici -S
undici 导出了一个对象,该对象提供了以下几个 API:
- undici.fetch : 发起请求,与 fetch 一致
- undici.request : 发起请求的请求库,该方法支持 Promise;
- undici.stream :处理文件流,可用于下载文件;
但是需要注意的是 undici 依赖的 Node 版本需要>= v16.8.0。
// 以下来自undici的版本判断
if (util.nodeMajor > 16 || (util.nodeMajor === 16 && util.nodeMinor >= 8)) {
let fetchImpl = null;
module.exports.fetch = async function fetch(resource) {
if (!fetchImpl) {
fetchImpl = require('./lib/fetch').fetch;
}
try {
return await fetchImpl(...arguments);
} catch (err) {
Error.captureStackTrace(err, this);
throw err;
}
};
//一系列导出对象,包括Headers、Response、Request、FormData、
// File、FileReader、setGlobalOrigin、getGlobalOrigin等等
}
下面重点介绍undici几个核心方法,即request、fetch、stream方法。
2.1 undici.request
下面示例展示了如何使用 request 发起请求,同时通过 for await 来获取请求结果。
import { request } from 'undici';
const { statusCode, headers, trailers, body } = await request(
'http://localhost:3000/foo'
);
console.log('response received', statusCode);
console.log('headers', headers);
for await (const data of body) {
console.log('data', data);
}
console.log('trailers', trailers);
2.2 undici.fetch
undici.fetch 使用方法与原生 fetch 类似,比如下面的示例使用 undici.fetch 发起一个 POST 请求。
const { fetch } = require('undici');
const getData = async () => {
const api = 'http://localhost:3100/login';
const rsp = await fetch(api, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
account: 'shenfq',
password: '123456',
}),
});
if (rsp.status !== 200) {
console.log(rsp.status, '请求失败');
return;
}
const json = await rsp.json();
console.log(rsp.status, json);
};
getData();
2.3 undici.stream
undici.steam 方法可用于下载文件或接口代理。比如下面是文件下载示例:
const fs = require('fs');
const { stream } = require('undici');
const out = fs.createWriteStream('./a.jpg');
const url = 'https://xxx.jpg';
stream(
url,
{
opaque: out,
},
(opaque) => opaque
);
下面是使用 undici 将 3100 端口的请求,代理到 80 端口的示例:
const http = require('http');
const undici = require('undici');
// 将 3100 端口的请求,代理到 80 端口
const client = new undici.Client('http://localhost');
http
.createServer((req, res) => {
const { url, method } = req;
client.stream({ method, path: url, opaque: res }, ({ opaque }) => opaque);
})
.listen(3100);
4.Body Mixins
body mixin 是格式化请求/响应主体的最常见方式。Mixins 包括:
- .formData()
- .json()
- .text() 等用法示例:
import { request } from 'undici';
const { statusCode, headers, trailers, body } = await request(
'http://localhost:3000/foo'
);
console.log('response received', statusCode);
console.log('headers', headers);
console.log('data', await body.json());
// 调用.json的mixin
console.log('trailers', trailers);
注意:一旦一个 mixin 被调用,body 就不能被重用,因此在 .body 上调用额外的 mixin,例如 .body.json(), .body.text() 将导致错误 “TypeError: unusable ”被抛出并通过 Promise reject 返回。
如果需要在使用 mixin 后以纯文本形式访问正文,最佳做法是首先使用 .text() mixin,然后手动将文本解析为所需格式。
5.undici的其他 API 方法
- undici.pipeline([url, options, ]handler):返回 stream.Duplex 对象
- undici.connect([url, options]):返回带有 Dispatcher.connect 方法结果的 Promise
- request.body:undici 的 body 可以是: ArrayBuffer、ArrayBufferView、AsyncIterables、Blob、 可迭代对象、字符串、URLSearchParams、FormData 等诸多类型。在 undici 这个 fetch 的实现中,request.body 甚至可以接受 Async Iterables,虽然它不存在于 Fetch 标准中。
import { fetch } from 'undici';
const data = {
async *[Symbol.asyncIterator]() {
yield 'hello';
yield 'world';
},
};
await fetch('https://example.com', {
body: data,
method: 'POST',
duplex: 'half',
});
- undici.upgrade([url, options]):返回带有 Dispatcher.upgrade 方法结果的 Promise
- undici.setGlobalDispatcher(dispatcher):设置通用 API 方法使用的全局调度程序(global dispatcher )。
6.本文总结
本文主要和大家介绍 undici ,即NodeJS fetch的底层实现。文章从什么是 undici 、快速使用 undici 、undici 常用方法等诸多维度展开。
但是,网上关于undici的资料真是非常少,文章也没有过多展开,如果有兴趣,可以在我的主页继续阅读,同时文末的参考资料提供了大量优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!
参考资料
https://undici.nodejs.org/#/?id=undici
https://segmentfault.com/a/1190000040837026/en
https://github.com/nodejs/undici
https://nodejs.medium.com/introducing-undici-4-1e321243e007
https://nodejs.dev/en/blog/announcements/v18-release-announce