Vite项目使用@turbodocx/html-to-docx报错问题排查与解决方案

一、问题概述

在 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负责落地验证的协作模式,高效解决开发与生产环境兼容问题,核心总结如下:

  1. 开发环境问题 :Vite 优先加载插件 CJS 产物导致默认导出缺失,且缺少 Node.js 内置的 events 模块浏览器垫片,最终通过通过强制指定插件ESM产物、引入 vite-plugin-node-polyfills 解决问题。
  2. 生产环境问题crypto-browserify 为 CJS 格式且存在循环依赖,与 Vite ESM 打包机制冲突,常规分包、打包优化方案均无法修复,最终通过使用轻量垫片替代 crypto 模块解决问题。
  3. 人机协作解决思路:首先由 AI 结合报错信息和错误堆栈分析代码,并结合问题的原因分析给出可行的解决方案,然后由开发者结合项目的实际情况和个人经验选择最佳方案让 AI 去执行,最后由 AI 自主完成配置改造、代码编写和方案验证。
  4. 协作价值与效果:依托「AI提供思路、人工决策把关、AI实施验证」的模式,规避了传统开发盲目调试、无效试错的问题,极大缩短复杂兼容问题的排查周期。
相关推荐
DigitalOcean7 小时前
为AI编程降本!OpenCode 原生支持 DigitalOcean 推理路由器
ai编程·claude
不爱说话郭德纲7 小时前
出门在外收到任务,我用 TRAE SOLO 把电脑“叫醒”干活
前端·ai编程
前端Hardy7 小时前
这个前端动画库,火了!
前端·javascript
Asmewill7 小时前
LangGraph学习笔记六(Stream流式输出)
前端
哈撒Ki7 小时前
前端性能优化汇总
前端·面试
Asmewill7 小时前
LangGraph学习笔记七(checkpointer)
前端
前端小木屋7 小时前
uniapp与蓝牙设备连接详细步骤
前端·微信小程序
yingyima7 小时前
Go 语言定时任务速查手册:实现延迟与周期任务的高效方法
前端
卷帘依旧7 小时前
npm包发布和管理流程(AI生成)
前端