为何Vite开发模式可正常解析jsonwebtoken,构建后却报错?
1. 开发与生产环境差异的根源
jsonwebtoken是Node.js专属的CommonJS模块,Vite在开发模式下会直接用Node.js原生模块加载机制处理它,此时import jwt from 'jsonwebtoken'能正确拿到完整的jwt对象,jwt.sign自然可用。
但生产构建时,Vite会把所有依赖打包成ES模块,而jsonwebtoken的内部导出逻辑没适配纯ES模块环境——它的module.exports是包含sign/verify等方法的对象,被Vite转成ES模块后,默认导出会被额外包裹一层,导致你import的jwt变成了仅含default属性的对象,直接调用jwt.sign就会触发“不是函数”的错误。
你找到的GitHub issue核心内容是:大量开发者在ES模块环境下使用jsonwebtoken时,都会遇到导出结构异常的问题,必须写成import * as jwt from 'jsonwebtoken'或者const jwt = require('jsonwebtoken')才能正常调用方法,本质就是CommonJS与ES模块的兼容性冲突。
2. 编译阶段检测这类错误的方法
- 给Vite添加
rollup-plugin-eslint插件,配置ESLint的node/no-unsupported-features/es-syntax规则,提前检测CommonJS模块在ES模块环境下的导入问题; - 在Vite配置中开启
build.commonjsOptions.transformMixedEsModules,强制Rollup正确转换混合模块,打包时若遇到导出异常会给出警告; - 本地预构建后用
vite preview提前验证生产代码,别等部署才暴露问题。
3. 修复构建配置的具体方案
最直接的解决方式是让Vite不对jsonwebtoken进行打包,保留原生Node.js模块加载逻辑,修改你的vite.config.js:
import { defineConfig } from 'vite'; import { nodePolyfills } from 'vite-plugin-node-polyfills'; export default defineConfig({ plugins: [nodePolyfills()], build: { outDir: 'dist', lib: { entry: 'index.js', formats: ['cjs'], fileName: 'index', }, rollupOptions: { // 添加该行,将jsonwebtoken设为外部依赖 external: ['jsonwebtoken'], }, }, });
这样构建后,代码会直接用Node.js的require加载jsonwebtoken,和开发模式的加载逻辑完全一致,就不会再出现方法找不到的问题。
另外也可以修改导入方式,把import jwt from 'jsonwebtoken'改成import * as jwt from 'jsonwebtoken'或者const jwt = require('jsonwebtoken'),但这种方式不如配置external彻底,后续其他Node模块可能也会遇到类似兼容性问题。
内容的提问来源于stack exchange,提问作者randyumi

