React Native Expo: 解决'crypto not found'错误
2025-01-25 00:20:05
解决 React Native Expo 中的 "crypto not found" 错误
在 React Native Expo 应用开发中,使用诸如 crypto
这类 Node.js 核心模块时,经常会遇到 ReferenceError: Property 'crypto' doesn't exist
这样的错误。 这个错误一般在直接使用 import * as crypto from 'crypto'
时发生,并且多数情况出现在 iOS 模拟器或者真机上使用 Xcode 运行时。该问题的核心原因在于,Expo 默认的 JavaScript 引擎(Hermes)并不直接支持 Node.js 的核心模块,这与 Node.js 环境本身不同。
原因分析
- 运行环境差异: 当你通过
npm run ios
启动应用时,Expo 通常会使用 Metro bundler 将你的 JavaScript 代码打包,而 Metro 知道如何处理 Node.js 模块的引入。但是当通过 Xcode 直接运行时,则可能跳过部分流程或使用不同的配置。 - Hermes 不兼容: Expo 应用通常在 iOS 设备上运行在 Hermes 引擎上,Hermes 不具备直接处理
crypto
模块的能力。crypto
模块本身依赖于 Node.js 的底层实现。 - 模块导入方式: 直接
import * as crypto from 'crypto'
通常适用于 Node.js 环境,但不适用于浏览器或者 React Native 的环境。
解决方案
理解错误产生的原因,我们可以通过以下几种方法解决问题:
方案一:使用 react-native-get-random-values 和 crypto-js
这个方法不直接依赖 Node.js 核心模块,而是使用 JavaScript 实现的替代库。 它在浏览器和 React Native 中工作良好。
操作步骤:
- 安装依赖: 使用 npm 或 yarn 安装
react-native-get-random-values
和crypto-js
:
npm install react-native-get-random-values crypto-js
- 在应用入口处引入
react-native-get-random-values
: 在应用的最顶层文件 (例如:App.js 或者 index.js) 的首行,添加引入代码,它会在内部创建一个全局crypto
对象供使用。 避免重复导入。
import 'react-native-get-random-values';
//你的其余的import 代码....
- 修改
crypto
相关代码 : 把原来导入的import * as crypto from 'crypto';
注释掉, 然后改用crypto-js
进行加密处理:
// import * as crypto from 'crypto'; //删除此行
import CryptoJS from 'crypto-js';
const encrypt = (payload: DeviceVerificationPayload) => {
const algorithm = 'aes-256-cbc';
const encryptionAlgoKey = 'Key';
const encryptionKey = base64ToUtf8(encryptionAlgoKey);
const key = CryptoJS.SHA256(encryptionKey);
const iv = CryptoJS.lib.WordArray.random(16); // 使用 crypto-js 生成随机IV
const stringifiedPayload = JSON.stringify(payload);
const cipher = CryptoJS.AES.encrypt(stringifiedPayload, key, {
iv: iv,
mode: CryptoJS.mode.CBC
});
return cipher.toString();
};
const base64ToUtf8 = (str:string): string =>{
return CryptoJS.enc.Base64.parse(str).toString(CryptoJS.enc.Utf8);
};
```
**安全提示:** 使用 `crypto-js` 时务必注意安全配置, 确保加密过程符合你的需求。并且密钥的管理也需额外重视, 可以采用环境变量或者其他的密钥安全管理方式.
4. **重新运行应用** : 使用 `npm run ios` 重新构建并运行应用程序,验证问题是否解决。如果Xcode还报一样的错误, 确保删掉你本地app的安装, 清空缓存,然后再在Xcode重新构建运行应用.
**原理说明:** `react-native-get-random-values` 主要功能是填充全局 `crypto` 对象提供加密需要的随机值功能,`crypto-js` 是纯JavaScript 版本的加密库,可以在 React Native 环境直接使用. 结合使用避免了直接依赖 Node.js 核心模块.
#### 方案二:使用 expo-crypto 库
`expo-crypto` 是 Expo 官方提供的一个封装加密功能的库,能够安全且便捷地在 React Native 应用中使用。这个方法需要移除旧的引入方式并且使用 expo 的版本。
**操作步骤:**
1. **安装依赖:**
```bash
npx expo install expo-crypto
-
修改
crypto
相关代码 : 移除原来的引入并改成如下使用 expo 版本,这里需要自行修改适配expo-crypto 的api。// import * as crypto from 'crypto'; 删除此行 import * as Crypto from 'expo-crypto'; const encrypt = async (payload: DeviceVerificationPayload) => { const algorithm = 'aes-256-cbc'; const encryptionAlgoKey = 'Key'; const encryptionKey = base64ToUtf8(encryptionAlgoKey); const key = await Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256,encryptionKey) const iv = await Crypto.getRandomBytesAsync(16) ; const stringifiedPayload = JSON.stringify(payload); const bufferPayload = new TextEncoder().encode(stringifiedPayload); const encrypted = await Crypto.cryptoEncryptAsync( { algorithm, key: new Uint8Array(Array.from(key)).buffer, iv : new Uint8Array(Array.from(iv)).buffer , } , new Uint8Array(Array.from(bufferPayload)).buffer ) return Buffer.from(encrypted).toString('hex') }; const base64ToUtf8 = (str:string): string =>{ return Crypto.Base64.decode(str) };
注意 :
expo-crypto
的 api 与 Node.js 核心模块的 api 有一定差别。需要根据文档来使用。expo-crypto
中所有的async
类型的函数, 请记得使用async
await
。同时一些方法的入参,要记得使用对应的 ArrayBuffer 进行数据传递。 -
重新运行应用: 重新构建并运行应用程序, 验证问题是否已经解决。 同样,请确保清空本地app 的安装缓存之后再进行测试。
原理说明:
expo-crypto
是 Expo 提供的安全加密库,在 Expo 环境有更好的兼容性. 它提供了多种加密方法和实用工具,在实际项目中更值得优先考虑。
其他考虑
-
模块冲突: 有时依赖之间可能存在版本冲突导致此问题。可以尝试更新或者降级相关依赖,或者清空 node_modules,重新安装依赖来解决。
-
调试困难: 如果调试过程中仍然出现错误,可利用 React Native 调试工具以及 Xcode 日志进行逐步排查,理清代码逻辑和运行步骤,精准定位错误源头。
以上两种方案均能解决 "crypto not found" 错误,你可以根据项目的具体情况以及对第三方库的偏好进行选择。 始终保持库版本的最新,定期审查更新文档,这样能避免很多未知的错误和风险。