【亲测好用】全套CommonJS的最佳解决方案

2026-10-12 来源: 点击量

【亲测好用】全套 CommonJS 最佳解决方案

一句话定位:CommonJS(CJS)是 Node.js 传统模块化,require/module.exports;痛点集中在Node ESM 混合报错、浏览器无法直接运行、库发布双包陷阱、循环依赖、打包兼容,下面是完整落地方案,覆盖业务项目、库开发、打包、避坑全套。【亲测好用】全套CommonJS的最佳解决方案

一、先分清:什么时候继续用 CommonJS

✅ 推荐 CJS 场景

老 Node 后端项目、CLI 工具、脚本工程,历史代码量大,迁移 ESM 成本高

依赖大量老旧 npm 包,包本身只提供 CJS

简单脚本,需要__dirname/__filename,不想额外处理 ESM 兼容

❌ 不推荐 CJS 场景

前端 Web 应用(Vue/React):优先 ESM,Tree-Shaking 效果更好

新开发开源库:优先 ESM;如需兼容旧项目再做分发

核心原则:一个项目内部统一模块类型,不要随意混合;Node 中package.json写"type":"commonjs",.js默认 CJS;.cjs强制 CJS,不受 type 字段影响webpack

二、方案 1:Node 项目纯 CommonJS(稳定首选)

package.json

{

"name": "cjs-demo",

"version": "1.0.0",

"type": "commonjs",

"main": "./index.js"

}

基础写法(标准,规避坑)

// lib.js

// ✅推荐:module.exports一次性导出,不要混用exports.xxx

module.exports = {

hello: () => "hello cjs",

num: 100

}

// index.js

const lib = require('./lib.js')

console.log(lib.hello())

❌坑:exports = {} 直接覆盖无效,exports 只是 module.exports 的引用。

读取文件路径(CJS 天然支持)

const path = require('path')

const fullPath = path.join(__dirname, './data.json')

运行命令

node index.js

三、方案 2:CJS 项目引入 ESM 包(最常见报错场景)

Node CJS 项目require()导入 ESM 包,直接报错。两套解决办法:

方案 A(推荐,简单):动态 import (),异步

// CJS index.js

async function run() {

const esmPkg = await import('esm-only-package')

console.log(esmPkg.default)

}

run()

方案 B:工具 esm 加载器(老项目临时兼容)

npm install esm

入口文件:

require = require("esm")(module);

const pkg = require("esm-package");

缺点:仅用于旧脚本,生产服务不推荐,性能有损耗。

四、方案 3:把 CommonJS 打包到浏览器运行(浏览器无 require)

浏览器原生不支持require(),必须打包。3 套亲测方案:

✅轻量首选:esbuild(速度最快)

npm install -D esbuild

# 打包CJS源码输出浏览器可用bundle

esbuild src/index.js --bundle --outfile=dist/bundle.js --format=iife

✅大型项目:Webpack5 处理 CJS

// webpack.config.js

const path = require('path')

module.exports = {

entry: './src/index.js',

output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' },

resolve: {

extensions: ['.js','.cjs'],

// node内置模块浏览器fallback

fallback: { path: require.resolve("path-browserify") }

}

}

✅库打包:Rollup + @rollup/plugin-commonjs

Rollup 默认 ESM,加载 CJS 依赖必须开启commonjs插件,解决第三方 CJS 包导入问题。

五、方案 4:开源库发布,同时支持 CJS & ESM(安全双包方案)

重点规避双包陷阱(dual package hazard):同一依赖同时加载两份实例,单例失效Node.js。 package.json 配置:

{

"main": "./dist/index.cjs",

"exports": {

".": {

"require": "./dist/index.cjs",

"import": "./dist/index.mjs"

}

}

}

源码建议:一套 TS 源码,构建同时输出 .cjs + .mjs(tshy /tsup 一键生成)

最佳实践:尽量避免默认导出,优先命名导出,减少 interop default 坑npm

六、高频问题全套解决方案(亲测踩坑清单)

1. require 找不到文件

CJS require 路径必须写./相对路径;require('lib')优先 node_modules

后缀可省略:.js/.cjs/.json自动识别

2. 循环依赖(CJS 经典坑)

CJS 是运行时加载,循环依赖会拿到未完成的 exports。解决办法:

重构代码,消除循环依赖(首选)

把导入放到函数内部,延迟 require

3. ESM 导入 CJS 包 default 导出异常

// ESM中导入CJS模块

import pkg from './cjs.cjs'

// 等价于 const { default: pkg } = require('./cjs.cjs')

CJS 导出整体挂载在default,解构要注意:

import { foo } from './cjs.cjs' // 可以,打包工具自动代理

4. __dirname /__filename 在 ESM 不存在

如果你混用 / 迁移到 ESM,这两个变量消失;CJS 天然自带,这也是很多老脚本保留 CJS 的原因。

5. CJS 无法 Tree-Shaking

CJS 动态 require,编译期不能分析依赖,打包只能做模块级剔除,无法精细删除无用代码。

结论:前端业务代码尽量 ESM;CJS 适合 Node 后端、CLI 脚本。

七、迁移路线:CJS → ESM(渐进方案,不想一次性大改)

项目 package.json 先不动type:commonjs,新增.mjs文件写新模块

新模块用 ESM,通过import()动态加载老 CJS 模块

全部代码改造完成后,统一改为"type":"module",文件后缀改成.js

八、选型总结(快速决策)

Node 后端 / CLI 脚本旧项目:保持 CommonJS,package.json type=commonjs

Node 项目必须引入 ESM-only 包:使用async import()

CJS 代码跑浏览器:esbuild 快速打包;大型项目 Webpack

开发 npm 工具库:TS 源码构建输出双格式 cjs/mjs,配置 exports 字段

前端 Web 项目:直接 ESM,不要 CJS

相关文章
  • 【亲测好用】全套CommonJS的最佳解决方案
    【亲测好用】全套CommonJS的最佳解决方案

    【亲测好用】全套 CommonJS 最佳解决方案一句话定位:CommonJS(CJS)是 Node.js 传统模块化,require/module.exports;痛点集中在Node ESM 混合报错、浏览器无法直接运行、库发布双包陷阱、循环依赖、打包兼容,下面是完整落地方案,覆盖业务项目、库开发、打包、避坑全套。一、...

  • 【亲测好用】跨域备忘单的最佳解决方案
    【亲测好用】跨域备忘单的最佳解决方案

    这是一份针对前端/全栈开发者的跨域(CORS)备忘单,覆盖原理、排查、解决方案与代码模板,建议收藏备用。一、 核心原理速查同源策略三要素:协议、域名、端口必须完全一致,任一不同即跨域。关键认知:跨域是浏览器的安全限制,请求本身能到达后端,但浏览...

  • JS搭建教程避坑指南:踩过的坑都给你标好了
    JS搭建教程避坑指南:踩过的坑都给你标好了

    “零基础原生搭建”到“现代工程化搭建”,再到“后端 Node.js 搭建”的完整教程。你可以根据自己的学习目标选择对应的路线:一、 原生基础搭建(适合初学者练手)如果你是刚开始学习,建议先从最基础的 HTML、CSS 和 JS 组合开始,这能帮你建立清晰的认知框架。创...

  • 入门Ionic在实际项目中的应用技巧
    入门Ionic在实际项目中的应用技巧

    结合你之前关注的 Typecho 响应式方案,可以看出你对前端工程化和跨端适配有不错的敏感度。Ionic 的核心正是利用 Web 技术栈(HTML/CSS/JS)构建跨平台应用,这种“一次编写,多端部署”的思路,对你来说应该很容易上手。第一步:环境搭建与项目初始化Ionic 的开发高度...

  • 【推荐收藏】前端测试框架推荐全套资源大合集
    【推荐收藏】前端测试框架推荐全套资源大合集

    前端测试框架的选型,核心在于匹配你的项目技术栈、测试层级(单元、集成、E2E)以及团队现有的开发习惯。结合目前的主流趋势,为你梳理了以下核心推荐:单元测试:Jest vs VitestJest:前端单元测试的“老牌标杆”。开箱即用,快照测试和 Mock 能力极强,生态最完...

  • 2026年Angular还值得学吗最新趋势与最佳实践
    2026年Angular还值得学吗最新趋势与最佳实践

    关于“Angular 还值得学吗”这个问题,答案并不是绝对的“是”或“否”,而是取决于你的职业规划和目标项目类型。简单来说:如果你想进大厂、做大型企业级项目,Angular 依然是一个极具竞争力的“硬通货”;但如果你是想快速接单、做个人项目或初创公司,它可...