一、问题概述
在 vite 项目下使用 TurboDocx/html-to-docx: HTML to DOCX converter 实现HTML转DOCX功能时,功能无法正常使用,报错信息:Uncaught (in promise) SyntaxError: The requested module '/node_modules/.vite/deps/@turbodocx_html-to-docx.js?v=5f40fdb8' does not provide an export named 'default' (at useExportWord.ts:5:8)
二、问题排查与解决方案
问题分析
通过 AI 辅助分析问题,最终确定该报错的核心为 Vite 模块解析规则与插件打包产物不兼容:
@turbodocx/html-to-docx的 Browserify 打包的CJS格式文件(html-to-docx.browser.js),无 ESM 默认导出;- 插件的 module 文件(
html-to-docx.esm.js)为标准 ESM 格式,包含完整默认导出; - Vite 在浏览器环境会优先读取
html-to-docx.browser.js,从而加载了不兼容的 CJS 产物,导致默认导出缺失报错。
解决方案
在 vite.config.ts 中通过 resolve.alias 强制使用 ESM 版本。
json
{
resolve: {
alias: [
// redirect to esm.js
{
find: "@turbodocx/html-to-docx",
replacement: path.resolve(
__dirname,
"node_modules/@turbodocx/html-to-docx/dist/html-to-docx.esm.js"
),
},
],
},
}
衍生报错
完成上述调整后,清理 vite 缓存重新启动服务,出现新的衍生报错:XMLBuilderCBImpl.ts:32 Uncaught (in promise) TypeError: Class extends value undefined is not a constructor or null
AI 协助定位原因:插件依赖的 xmlbuilder2 模块依赖 Node.js 内置 events 模块(EventEmitter), 而 Vite 浏览器构建环境不支持 Node.js 内置模块,导致模块加载异常,AI 提供了三个可行解决方案:
- 使用插件UMD打包产物(已内置所有依赖);
- 通过Vite别名单独配置
events模块Polyfill; - 引入
vite-plugin-node-polyfills插件,批量补齐Node.js内置模块浏览器垫片。
为彻底解决插件的兼容性问题,选择第三种方案,完整Vite开发配置如下,清理 vite 缓存并重启服务后开发环境可正常实现HTML转DOCX导出功能
javascript
import { defineConfig } from "vite";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import path from "path";
export default defineConfig(({ mode }) => {
return {
define: {
// process version and browser is necessary !
"process.version": JSON.stringify("v16.0.0"),
"process.browser": "true",
},
plugins: [
// Node.js polyfill(html-to-docx → xmlbuilder2 needed it)
nodePolyfills(),
],
resolve: {
alias: [
// redirect to esm.js
{
find: "@turbodocx/html-to-docx",
replacement: path.resolve(
__dirname,
"node_modules/@turbodocx/html-to-docx/dist/html-to-docx.esm.js"
),
},
],
},
};
});
三、生产构建问题排查与解决
上述方案解决了开发环境下的问题,但是构建打包后部署到生产环境上报了新的错误:
java
Index-a0d49d48.js:1 ReferenceError: exports is not defined at requireBrowser (npm.crypto-js-4d88e432.js:5:102949)
at requireCryptoBrowserify (npm.crypto-js-4d88e432.js:5:105502)
at html-to-docx.esm-e4dcbb63.js:1:55539
问题分析
让AI基于上述错误继续排查问题,最开始 AI 的解决思路方向是将 crypto 这个包的依赖链打到一个 chunk 包里面,但是该包为 CJS 格式且存在循环依赖,Vite ESM 打包环境与 CJS 模块存在互操作冲突,无论拆分Chunk、调整打包策略均无法解决,最终导致生产环境exports未定义报错,最后 AI 给出了另一个解决思路把问题解决了,核心思路如下:
- 单独排除
crypto模块的 Polyfill,规避 CJS 循环依赖; - 自定义极简 ESM 垫片,仅实现
html-to-docx所需的randomFillSync方法,基于浏览器原生API实现,无第三方依赖; - 通过Vite别名替换原生
crypto模块,统一适配ESM打包环境。
最终解决方案
1. 新增 Crypto ESM垫片文件
新建src/shims/crypto.ts,提供轻量浏览器兼容实现:
javascript
/**
* crypto 模块的浏览器 ESM shim
* 仅提供 html-to-docx 所需的 randomFillSync 方法
* 替代 vite-plugin-node-polyfills 的 crypto-browserify(CJS 循环依赖会导致 exports is not defined)
*/
function randomFillSync(buffer: Uint8Array): Uint8Array {
if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
window.crypto.getRandomValues(buffer)
} else {
for (let i = 0; i < buffer.length; i++) {
buffer[i] = Math.floor(Math.random() * 256)
}
}
return buffer
}
export default { randomFillSync }
export { randomFillSync }
2. .vite 完整配置
typescript
import { defineConfig } from "vite";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import path from "path";
export default defineConfig(({ mode }) => {
return {
define: {
// process version and browser is necessary !
"process.version": JSON.stringify("v16.0.0"),
"process.browser": "true",
},
plugins: [
// Node.js 内置模块 polyfill(html-to-docx → xmlbuilder2/nanoid 等依赖链需要)
// 排除 crypto:crypto-browserify 是 CJS 包且有循环依赖,打包会触发 exports is not defined
// 改用 src/shims/crypto.ts 提供轻量 ESM shim(仅 randomFillSync)
nodePolyfills({ exclude: ['crypto'] }),
],
resolve: {
alias: [
// redirect to esm.js
{
find: "@turbodocx/html-to-docx",
replacement: path.resolve(
__dirname,
"node_modules/@turbodocx/html-to-docx/dist/html-to-docx.esm.js"
),
},
// crypto 模块 ESM shim:替代 crypto-browserify(CJS 循环依赖导致 exports is not defined)
{
find: 'crypto',
replacement: path.resolve(__dirname, 'src/shims/crypto.ts'),
},
],
},
};
});
四、总结
本次问题源于Vite ESM构建、Node内置模块、CJS/ESM跨模块兼容 的多重问题,依赖嵌套深、报错隐蔽。本次采用AI提供排查方向和思路,然后由人判断并决策使用的解决方案,最后由AI负责落地验证的协作模式,高效解决开发与生产环境兼容问题,核心总结如下:
- 开发环境问题 :Vite 优先加载插件 CJS 产物导致默认导出缺失,且缺少 Node.js 内置的
events模块浏览器垫片,最终通过通过强制指定插件ESM产物、引入vite-plugin-node-polyfills解决问题。 - 生产环境问题 :
crypto-browserify为 CJS 格式且存在循环依赖,与 Vite ESM 打包机制冲突,常规分包、打包优化方案均无法修复,最终通过使用轻量垫片替代crypto模块解决问题。 - 人机协作解决思路:首先由 AI 结合报错信息和错误堆栈分析代码,并结合问题的原因分析给出可行的解决方案,然后由开发者结合项目的实际情况和个人经验选择最佳方案让 AI 去执行,最后由 AI 自主完成配置改造、代码编写和方案验证。
- 协作价值与效果:依托「AI提供思路、人工决策把关、AI实施验证」的模式,规避了传统开发盲目调试、无效试错的问题,极大缩短复杂兼容问题的排查周期。