再见,Node.js 缓冲区
从一开始,Buffer 类型就一直是 Node.js 中二进制数据处理的基石。 然而,现在我们有了 Uint8Array
,它是一种原生 JavaScript 类型,可以跨平台工作 。 虽然“Buffer”是“Uint8Array”的一个实例,但它引入了许多在其他 JavaScript 环境中不可用的方法。 因此,利用特定于 Buffer 的方法的代码需要进行多填充,从而阻止许多有价值的包与浏览器兼容。
“Buffer”还带有额外的警告。 例如, Buffer#slice()
创建一个链接到原始 Buffer
的可变段,而 Uint8Array#slice( )
创建一个不可变的副本,从而可能导致不可预测的行为。 问题不在于 Buffer#slice() 方法的行为,而在于 Buffer 是 Uint8Array 的子类,并且完全改变了继承方法的行为。 使用“Uint8Array#subarray()”或“Buffer#subarray()”代替“Buffer#slice()”。 此外,缓冲区通过全局变量暴露私人信息,存在潜在的安全风险。
是时候继续前进了。
## 计划
我打算将我的所有包从使用Buffer
移动到Uint8Array
。 如果您是 JavaScript 包的维护者,我鼓励您也这样做。
“Buffer”永远不会被删除,甚至可能永远不会被弃用,但至少社区可以慢慢摆脱它。 我希望 Node.js 团队至少开始阻止使用“Buffer”。
## 如何
首先,熟悉“Uint8Array”和“Buffer”之间的微妙的不兼容性。
我制作了 uint8array-extras
包 以使转换更容易。 欢迎请求其他实用程序。
如果您的代码接受“Buffer”并且不使用任何“Buffer”特定的方法,您只需将文档和类型更新为“Uint8Array”即可。 将输入类型从“Buffer”更改为“Uint8Array”是一项非重大更改,因为“Buffer”是“Uint8Array”的实例。
将返回类型从“Buffer”更改为“Uint8Array”是一项重大更改,因为消费者可能会使用“Buffer”特定的方法。
如果您绝对需要将 Uint8Array
转换为 Buffer
,您可以使用 Buffer.from(uint8Array)
(复制数据)或 Buffer.from(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength)
(不复制)。 然而,通常有更好的方法。
主要过渡步骤是:
- 删除所有
import {Buffer} from 'node:buffer'
导入。 - 删除所有出现的“Buffer”全局变量。
- 停止使用特定于“Buffer”的方法。
- 将
Buffer
读/写方法替换为 [DataView
](https://developer .mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView)。
### 问题
为什么要存在“Buffer”?
“Buffer”是在“Uint8Array”存在之前很久就创建的。
如何使用 Uint8Array
与 Base64 相互转换?
您现在可以使用我的uint8array-extras
包。 它很可能最终会在 JavaScript 中得到原生支持。
如何处理返回 Buffer
的 Node.js API,例如 fs
方法?
由于“Buffer”是“Uint8Array”的子类,因此您可以将其视为“Uint8Array”。 只需确保您不使用“.slice()”(其行为不同)或任何特定于缓冲区的方法。
### 例子
JavaScript
import {stringToBase64} from 'uint8array-extras';
Buffer.from(string).toString('base64');
stringToBase64(string);
import {uint8ArrayToHex} from 'uint8array-extras';
buffer.toString('hex');
uint8ArrayToHex(uint8Array);
const bytes = getBytes();
const view = new DataView(bytes.buffer);
const value = bytes.readInt32BE(1);
const value = view.getInt32(1);
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
,因此不需要额外的工作。 理想情况下,此代码还应转换为 Web Crypto,但这与本示例无关。
打字稿
import {Buffer} from 'node:buffer';
export function getSize(input: string | Buffer): number { … }
export function getSize(input: string | Uint8Array): number { … }
执行
我建议通过 linting 强制使用“Uint8Array”而不是“Buffer”。
将其添加到您的 ESLint 配置中:
{
'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-eslint/ban-types': [
'error',
{
types: {
Buffer: {
message: 'Use Uint8Array instead.',
suggest: [
'Uint8Array'
]
}
}
}
]
}
如果您使用XO,它很快就会默认带有此配置。
我可以帮忙吗?
表达你的支持 对于使用 Uint8Array
的 Node.js 新 API。
帮我将我的包移动到Uint8Array
。 选择一个并尝试一下。
帮助我们制定一个 lint 规则 以防止使用 Buffer
方法。
## 未来
Uint8Array
(或者更确切地说,TypedArray
)需要更多实用方法!
例如,目前没有好的内置方法可以将 Uint8Array
转换为 Base64 或 Hex。 尽管如此,看起来这很可能会出现。
考虑向 TC39 提出缺失的位。
## 结束
让我们让 JavaScript 包生态系统更加跨平台。 谢谢阅读。