声明:(1)此文为译文,原文见 Goodbye, Node.js Buffer (2)翻译不准处的后面加上了英文原文词汇或进行了不影响理解的删减(3)翻译不对的地方请不吝赐教,留言更正(4)为了使文章更具有可读性,译者适当地增加了标题和划分了段落。
Buffer存在的缺欠
Buffer类型从一开始就是Node.js中处理二进制数据的基石。然而,现在我们有了 Uint8Array,这是一个原生的 JavaScript 类型并且可以跨平台工作。而 Buffer 是 Uint8Array 的一个实例,它引入了许多在其他 JavaScript 环境中不可用的方法。因此,那些使用了特定于Buffer的方法(Buffer-specific methods)的相关代码需要进行填充(polyfilling),这阻碍了许多有价值的包和浏览器兼容。
Buffer 还带了一些额外的问题。例如:
第一,Buffer的slice()方法,创建了一个链接到原始缓冲区的可变片段(segment ),会导致一些不可预测的行为,然而 Uint8Array的slice()方法则创建了一个不可变的副本。问题不在于Buffer的slice()方法的行为,而在于Buffer是Uint8Array的子类,它完全改变了继承自Uint8Array的方法的行为。可以使用Unit8Array的subarray()方法或者Buffer的subarray()方法代替Buffer的slice()方法。
第二,Buffer通过全局变量暴露隐私信息,这是一个潜在的安全风险。
我们是时候和Buffer说拜拜了!
我的升级计划
我打算将我所有的包从使用 Buffer 改为Uint8Array。如果您是 JavaScript 包的维护者,我鼓励您也这样做。Buffer可能永远不会被删除,甚至可能永远不会被弃用,但至少社区可以慢慢远离它,我希望 Node.js 团队至少开始不鼓励使用 Buffer。
如何进行升级
首先,熟悉 Uint8Array 和 Buffer 之间微妙的不兼容性。我已经开发了uint8array-extras包来使转换更加容易。
如果你的代码使用了 Buffer 并且没有使用任何特定于 Buffer 的方法( Buffer-specific methods),那么只需将文档和数据类型更新为 Uint8Array 即可。
将输入类型从 Buffer 更改为 Uint8Array 是非破坏性更改,因为 Buffer 是 Uint8Array 的实例。
将返回类型从 Buffer 更改为 Uint8Array 是一个重大更改,因为返回值的消费者可能使用特定于Buffer的方法(Buffer-specific methods)。
如果你确实需要将 Uint8Array 转换为 Buffer,可以使用 Buffer.from (uint8Array)(拷贝数据)或者 Buffer.from(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength)(不拷贝数据)。然而还有更好的方法吗?
一些主要的修改步骤是:
- 删除所有的
import {Buffer} from 'node:buffer'
导入语句 - 删除所有的Buffer全局变量
- 停止使用特定于Buffer的方法
- 替换 Buffer的读/写方法,例如用DataView替换Buffer的readInt32BE()方法
解答一些问题
1.Buffer 为什么一开始会存在?
在 Uint8Array 存在之前很久就已经创建了 Buffer。
2.如何使用 Uint8Array 进行与 Base64之间的转换?
你现在可以使用我的 uint8array-extras 包,它很可能最终会在 原生JavaScript 中得到支持。
3.如何处理返回Buffer的 Node.js API,比如 fs 的方法?
因为 Buffer 是 Uint8Array 的一个子类,所以您可以将它看作 Uint8Array。只要确保不使用. slice ()(它在行为上有所不同)或任何特定于 Buffer 的方法( Buffer-specific methods)。
例子
Javascript代码
JavaScript
+ import {stringToBase64} from 'uint8array-extras';
- Buffer.from(string).toString('base64');
+ stringToBase64(string);
JavaScript
+ import {uint8ArrayToHex} from 'uint8array-extras';
- buffer.toString('hex');
+ uint8ArrayToHex(uint8Array);
JavaScript
const bytes = getBytes();
+ const view = new DataView(bytes.buffer);
- const value = bytes.readInt32BE(1);
+ const value = view.getInt32(1);
JavaScript
import crypto from 'node:crypto';
- import {Buffer} from 'node:buffer';
+ import {isUint8Array} from 'uint8array-extras';
export default function hash(data) {
- if (!(typeof data === 'string' || Buffer.isBuffer(data))) {
+ if (!(typeof data === 'string' || isUint8Array(data))) {
throw new TypeError('Incorrect type.');
}
return crypto.createHash('md5').update(data).digest('hex');
}
大多数 Node.js API 也接受 Uint8Array,因此不需要额外的工作。
TypeScript代码
TypeScript
- import {Buffer} from 'node:buffer';
- export function getSize(input: string | Buffer): number { ... }
+ export function getSize(input: string | Uint8Array): number { ... }
强制使用Unit8Array
我建议使用 lint工具(linting)强制使用 Uint8Array 而不是 Buffer,将如下内容增加到你的ESLint配置当中:
JavaScript
{
'no-restricted-globals': [
'error',
{
name: 'Buffer',
message: 'Use Uint8Array instead.'
}
],
'no-restricted-imports': [
'error',
{
name: 'buffer',
message: 'Use Uint8Array instead.'
},
{
name: 'node:buffer',
message: 'Use Uint8Array instead.'
}
]
}
如果你使用的是TypeScript则使用如下内容:
TypeScript
{
'@typescript-eslint/ban-types': [
'error',
{
types: {
Buffer: {
message: 'Use Uint8Array instead.',
suggest: [
'Uint8Array'
]
}
}
}
]
}