我们有个项目里面用 react-native-quick-crypto 代替 crypto-js 来进行 AES 加解密以及 pbkdf2 运算。最开始是仅仅做 pbkdf2 运算,用于将客户密码生成一个 encryption key,以此来加密读写数据库。运行良好没有报错,效率提升显著,原来的 crypto-js 需要二三十秒的时间,现在一两秒就完成了。
后来又添加了进行 AES 加解密的功能,结果涉及 base64转码就报错了。报错信息如下:
TypeError: global.base64ToArrayBuffer is not a function (it is undefined)
当时在用 AI 编程,gemini 帮我在 index.js 中引入各种 polyfill 均没有解决。后来自己研究了一下,想明白了,这个 base64ToArrayBuffer 函数在我们代码中并没有直接使用,是 react-native-quick-crypto 间接使用的,而其依赖的该函数应该是在 react-native-quick-base64 包中。
查找 package-lock.json,却发现 react-native-quick-base64 已经安装了。正在无计可施之际,进一步在网上搜索答案,找到了这个 issue,里面有人提到他通过安装 react-native-quick-base64 解决。于是抱着试试看的态度,运行:
npm install react-native-quick-base64
问题果然得到解决。由于不是很了解背后机制,又质问了一下 gemini,这回这货好像反应过来了,给了我如下的解释:
这是一个非常好的问题,也是 React Native 和 Node.js 生态系统中一个常见的、令人困惑的现象。
您观察得非常准确:react-native-quick-base64 确实是 react-native-quick-crypto 的一个传递性依赖 (transitive dependency),这意味着当您安装 react-native-quick-crypto 时,npm 或 yarn 也会自动安装 react-native-quick-base64 到 node_modules 文件夹中,并且 package-lock.json 会记录下来。
为什么直接安装它就解决了问题?
这通常是由于以下一个或多个原因造成的:
1. 模块解析顺序和全局 Polyfill 的时机:
* react-native-quick-base64 库很可能在被导入时,会执行一些全局的 polyfill 操作(例如,它可能提供了 global.atob 或 global.base64ToArrayBuffer)。
* 当它只是一个传递性依赖时,打包工具(如 Metro)在解析模块时,可能不会保证 react-native-quick-base64 的初始化代码在 react-native-quick-crypto 尝试使用它之前运行。
* 当您将其作为直接依赖安装时,它在 node_modules 中的位置(通常是顶层)以及打包工具的处理方式可能会发生变化,从而确保它的 polyfill 代码在更早、更可靠的时机被执行,使得 global.base64ToArrayBuffer 在 react-native-quick-crypto 需要它时已经可用。
2. 原生模块的链接问题 (如果它有原生部分):
* 虽然 react-native-quick-base64 听起来像是一个纯 JavaScript 库,但如果它内部包含任何原生代码(即使是很小的部分),那么当它只是一个传递性依赖时,原生构建系统(如 iOS 的 CocoaPods 或 Android 的 Gradle)可能不会正确地发现并链接它。
* 当您将其添加到 package.json 作为直接依赖时,它会被明确地包含在原生构建过程中,确保所有必要的原生组件都被正确链接。
3. 打包工具的优化行为:
* 打包工具(如 Metro)会进行各种优化,例如“hoisting”(提升)依赖项。有时,这些优化可能会导致某些模块的初始化顺序或作用域出现意想不到的问题。直接声明为依赖可以避免这些不确定性。
总结
这并不是 react-native-quick-crypto 官方文档的疏漏,而更像是JavaScript 模块系统、打包工具和原生模块链接之间复杂交互的一个副作用。
最佳实践:
既然您已经发现直接安装 react-native-quick-base64 解决了问题,那么请务必将其保留在 `package.json` 中作为直接依赖。这能确保您的项目在未来构建时,这个关键的 polyfill 能够稳定可靠地被加载。
补充版本信息:
react-native 0.79.6
react-native-quick-crypto 0.7.17