You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular18开发与生产环境CommonJS动态导入结果不一致问题

Angular 18 新构建系统下 CommonJS 模块动态导入的双 Default 问题

问题背景

Angular 18 切换到新构建系统后,本地开发依赖 Vite(底层基于 Rollup),生产构建使用 application 目标(底层基于 esbuild)。项目中使用 ngx-quill 搭配 CommonJS 规范的自定义模块 @enzedonline/quill-blot-formatter2,懒加载该模块时出现环境差异:

  • 开发环境:动态导入后 qbf.default 直接指向模块本身
  • 生产环境:qbf.default 内部嵌套了一层额外的 default(即双 default 问题)

原配置代码如下:

import { defer } from 'rxjs';
import { QuillConfig } from 'ngx-quill';

export const quillConfig: QuillConfig = {
  theme: 'snow',
  format: 'json',
  // ...其他配置项
  customModules: [
    {
      path: 'modules/blotFormatter',
      implementation: defer(() =>
        import('@enzedonline/quill-blot-formatter2/dist/BlotFormatter').then(qbf => {
          console.log(qbf);
          return qbf.default;
        })
      )
    }
  ]
};

差异产生的核心原因

这是 Rollup(Vite 底层)与 esbuild 对 CommonJS 模块转 ES 模块时,__esModule 标志的处理逻辑不一致导致的:

  • Rollup 会自动识别 CommonJS 模块是否带有 __esModule: true 标记,若存在则直接将模块的 exports.default 映射为 ES 模块的 default 导出,不会产生嵌套
  • esbuild 对无 __esModule 标记的纯 CommonJS 模块转换更"直白":如果原模块的导出结构是 exports.default = xxx,esbuild 会保留这层结构,最终导致动态导入后出现 default.default 的嵌套情况

你的 @enzedonline/quill-blot-formatter2 属于纯 CommonJS 模块,未正确设置 __esModule 标记,因此触发了两个构建工具的处理差异。

更合理的解决方案

方案1:通用导出提取工具函数

不用复杂类型判断,通过通用函数统一兼容两种环境的导出结构:

import { defer } from 'rxjs';
import { QuillConfig } from 'ngx-quill';

// 通用工具:提取 CommonJS/ES 模块的正确默认导出
const getDefaultExport = <T>(module: T) => {
  return (module as any).default?.default ?? (module as any).default ?? module;
};

export const quillConfig: QuillConfig = {
  theme: 'snow',
  format: 'json',
  // ...其他配置项
  customModules: [
    {
      path: 'modules/blotFormatter',
      implementation: defer(() =>
        import('@enzedonline/quill-blot-formatter2/dist/BlotFormatter').then(getDefaultExport)
      )
    }
  ]
};

方案2:调整 esbuild 转换配置

在 angular.json 中给生产构建的 esbuild 添加配置,让它对 CommonJS 模块的转换逻辑贴近 Rollup:

{
  "projects": {
    "你的项目名称": {
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:application",
          "options": {
            "outputPath": "dist/你的项目",
            // ...其他配置
            "esbuildOptions": {
              "commonjsOptions": {
                "transformMixedEsModules": true
              }
            }
          }
        }
      }
    }
  }
}

transformMixedEsModules: true 会让 esbuild 对无 __esModule 标记的 CommonJS 模块进行智能转换,减少导出结构差异。

方案3:直接使用模块的 ES 版本

检查 @enzedonline/quill-blot-formatter2 的 package.json,若存在 module 或 exports 字段定义的 ES 模块入口,直接导入该版本:

// 替换原导入路径为 ES 模块入口(示例路径,以实际包为准)
import('@enzedonline/quill-blot-formatter2/es/BlotFormatter').then(qbf => qbf.default)

从根源上避免 CommonJS 转 ES 模块的兼容问题。

内容的提问来源于stack exchange,提问作者uppy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 16:40:20