NextJS 12 + Expo生产构建报错:无法在模块外使用import语句
问题描述
在Next.js 12结合Expo构建生产版本时,出现以下错误:
info - Creating an optimized production build info - Compiled successfully info - Collecting page data .node:internal/process/promises:279 triggerUncaughtException(err, true /* fromPromise */); ^ /Users/myName/Desktop/Projects/AppName/Frontend/Next/node_modules/expo-modules-core/build/index.js:1 import { DeviceEventEmitter } from 'react-native'; ^^^^^^ SyntaxError: Cannot use import statement outside a module at Object.compileFunction (node:vm:360:18) at wrapSafe (node:internal/modules/cjs/loader:1084:15) at Module._compile (node:internal/modules/cjs/loader:1119:27) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1209:10) at Module.load (node:internal/modules/cjs/loader:1033:32) at Function.Module._load (node:internal/modules/cjs/loader:868:12) at Module.require (node:internal/modules/cjs/loader:1057:19) at require (node:internal/modules/cjs/helpers:103:18) at Object.21968 (/Users/myName/Desktop/Projects/AppName/Frontend/Next/apps/next/.next/server/pages/_app.js:1232:18) at __webpack_require__ (/Users/myName/Desktop/Projects/AppName/Frontend/Next/apps/next/.next/server/webpack-runtime.js:25:43) { type: 'SyntaxError' }
当前next.config.js配置:
/** @type {import('next').NextConfig} */ const { withNativebase } = require('@native-base/next-adapter'); const withImages = require('next-images'); const { withExpo } = require('@expo/next-adapter'); const withFonts = require('next-fonts'); module.exports = withNativebase({ dependencies: [ '@expo/next-adapter', 'next-images', 'next-fonts', 'react-native-vector-icons', 'react-native-vector-icons-for-web', 'solito', 'app', '@native-base/icons', ], plugins: [ [withFonts, { projectRoot: __dirname }], withImages, [withExpo, { projectRoot: __dirname }], ], reactStrictMode: true, images: { domains: ['localhost'], }, nextConfig: { typescript: { ignoreBuildErrors: true, }, eslint: { ignoreDuringBuilds: true, }, images: { disableStaticImages: true, }, projectRoot: __dirname, reactStrictMode: true, webpack: (config, options) => { config.module.rules.map((rule) => { if (rule.test !== undefined && rule.test.source.includes('|svg|')) { rule.test = new RegExp(rule.test.source.replace('|svg|', '|')); } }); config.module.rules.push({ test: /\.ttf$/, loader: 'url-loader', }); config.module.rules.push({ test: /\.otf$/, loader: 'url-loader', }); config.module.rules.push({ test: /\.svg$/, use: ['@svgr/webpack'], }); config.module.rules.push({ test: /\.mp4$/, use: [ { loader: 'url-loader', }, ], }); config.module.rules.push({ test: /\.html$/, loader: 'html-loader', }); config.resolve.alias = { ...(config.resolve.alias || {}), 'react-native$': 'react-native-web', '@expo/vector-icons': 'react-native-vector-icons', }; config.resolve.extensions = [ '.web.js', '.web.ts', '.web.tsx', ...config.resolve.extensions, ]; return config; }, }, });
已尝试使用withTM转译器,但导致字体及大量代码失效;另有相似配置项目可正常运行,目前无法定位问题。
解决方案
1. 核心原因定位
错误源于expo-modules-core使用ES模块语法,但Next.js服务端默认不转译node_modules内的包,服务端以CommonJS环境运行时无法识别import语句。
2. 具体修复步骤
(1)添加转译包配置
在nextConfig中添加transpilePackages,指定需要转译的Expo相关包:
nextConfig: { // ...其他原有配置 transpilePackages: ['expo-modules-core', 'react-native'], }
(2)调整插件加载顺序
将withExpo移至插件列表最前,确保Expo的配置优先生效:
plugins: [ [withExpo, { projectRoot: __dirname }], [withFonts, { projectRoot: __dirname }], withImages, ],
(3)修复SVG规则冲突
替换原有的SVG规则修改逻辑,避免和其他插件的配置冲突:
// 替换原config.module.rules.map(...)部分代码 const fileLoaderRule = config.module.rules.find(rule => rule.test && rule.test.test('.svg')); if (fileLoaderRule) { fileLoaderRule.exclude = /\.svg$/; }
(4)添加依赖到nativebase的dependencies列表
将expo-modules-core加入dependencies数组,确保nativebase适配器能正确识别:
dependencies: [ // ...原有依赖 'expo-modules-core', ],
3. 完整修改后的next.config.js
/** @type {import('next').NextConfig} */ const { withNativebase } = require('@native-base/next-adapter'); const withImages = require('next-images'); const { withExpo } = require('@expo/next-adapter'); const withFonts = require('next-fonts'); module.exports = withNativebase({ dependencies: [ '@expo/next-adapter', 'next-images', 'next-fonts', 'react-native-vector-icons', 'react-native-vector-icons-for-web', 'solito', 'app', '@native-base/icons', 'expo-modules-core', ], plugins: [ [withExpo, { projectRoot: __dirname }], [withFonts, { projectRoot: __dirname }], withImages, ], reactStrictMode: true, images: { domains: ['localhost'], }, nextConfig: { typescript: { ignoreBuildErrors: true, }, eslint: { ignoreDuringBuilds: true, }, images: { disableStaticImages: true, }, projectRoot: __dirname, reactStrictMode: true, transpilePackages: ['expo-modules-core', 'react-native'], webpack: (config, options) => { // 修复SVG规则冲突 const fileLoaderRule = config.module.rules.find(rule => rule.test && rule.test.test('.svg')); if (fileLoaderRule) { fileLoaderRule.exclude = /\.svg$/; } config.module.rules.push({ test: /\.ttf$/, loader: 'url-loader', }); config.module.rules.push({ test: /\.otf$/, loader: 'url-loader', }); config.module.rules.push({ test: /\.svg$/, use: ['@svgr/webpack'], }); config.module.rules.push({ test: /\.mp4$/, use: [ { loader: 'url-loader', }, ], }); config.module.rules.push({ test: /\.html$/, loader: 'html-loader', }); config.resolve.alias = { ...(config.resolve.alias || {}), 'react-native$': 'react-native-web', '@expo/vector-icons': 'react-native-vector-icons', }; config.resolve.extensions = [ '.web.js', '.web.ts', '.web.tsx', ...config.resolve.extensions, ]; return config; }, }, });
4. 额外排查建议
- 对比正常运行项目的
expo-modules-core、react-native-web版本,确保版本一致 - 删除
.next文件夹和node_modules,重新执行npm install和npm run build - 检查是否有其他未转译的ES模块依赖,如有可添加到
transpilePackages中
内容的提问来源于stack exchange,提问作者Steven Matthews
相关产品推荐
相关产品推荐

