js中common.js和ECMAScript.js区别

06-02 1650阅读

以下是关于 CommonJS 和 ECMAScript Modules(ESM)的详细对比分析,包含底层原理和示例说明:


🧩 核心差异对比表

特性CommonJSES Modules
来源Node.js 社区规范ECMAScript 语言标准
加载方式动态加载(运行时解析)静态加载(编译时解析)
加载环境Node.js 原生支持浏览器原生支持,Node.js需开启 --experimental-modules(v13.2+已稳定)
语法格式require() / module.exportsimport / export
加载行为同步加载异步加载
模块解析文件路径需完整支持 bare module 说明符(需要导入映射)
变量访问修改原始导出对象绑定只读引用
循环引用处理部分加载(未完成的状态)引用预解析(存在TDZ)
顶层作用域模块内this指向module.exports顶层this为undefined
静态分析不支持 Tree-shaking支持 Tree-shaking 优化

📦 底层加载机制差异(图示)

CommonJS 运行时解析流程
1. 执行代码 → 2. 构建模块对象 → 3. 按需加载依赖 → 4. 包裹成函数执行

Module Wrapper 伪代码:

function (exports, require, module, __filename, __dirname) {
  // 用户代码在此执行
  module.exports = ...;
}
ESM 预解析流程
1. 解析阶段 → 2. 建立模块关系图 → 3. 编译阶段 → 4. 实例化 → 5. 执行代码

关键特性:

  • 模块记录(Module Record):存储导入/导出关系
  • 实时绑定(Live Bindings):导出值变化会同步到导入方

    🛠️ 代码示例对比

    模块导出差异
    // CommonJS 动态修改
    exports.a = 1;        // ⇨ { a: 1 }
    module.exports = { b: 2 };  // 最终导出 { b: 2 }
    // ESM 绑定不可变
    export let count = 0;
    export function increment() {
      count++;  // 所有导入模块都会看到更新后的值
    }
    
    循环依赖处理
    // commonjs/a.js
    console.log('a开始');
    exports.done = false;
    const b = require('./b');  // 此时b尚未完成加载
    console.log('在a中,b.done =', b.done);
    exports.done = true;
    console.log('a结束');
    // commonjs/b.js
    console.log('b开始');
    exports.done = false;
    const a = require('./a');  // 此时a导出{done: false}
    console.log('在b中,a.done =', a.done);
    exports.done = true;
    console.log('b结束');
    
    # 执行结果:
    a开始 → b开始 → 在b中,a.done = false → b结束 → 在a中,b.done = true → a结束
    

    ⚡ 现代项目中的互操作性

    混合使用解决方案
    // 在 ESM 中引入 CJS
    import cjsModule from './commonjs-module.cjs';
    // 在 CJS 中引入 ESM(需异步)
    const esModule = await import('./es-module.mjs');
    
    Package.json 配置
    {
      "type": "module",       // 默认使用ESM
      "main": "./index.cjs",  // CJS入口
      "exports": {
        "import": "./esm/index.js",   // ESM入口
        "require": "./cjs/index.js"   // CJS入口
      }
    }
    

    🔧 转译工具处理原理(以Babel为例)

    # 转换步骤示例
    ESM → 解析为AST → 检测import/export → 替换为require语法 → 添加helper函数
    

    示例转换效果:

    // 原始ESM
    import { readFile } from 'fs';
    export const data = readFile('./file.txt');
    // 转换后CommonJS
    const { readFile } = require('fs');
    exports.data = readFile('./file.txt'); 
    

    🚀 性能优化差异

    1. CommonJS 优化难点

      • 无法预知依赖关系,阻碍并行加载
      • 动态表达式导致死代码难以消除
        require(condition ? 'a' : 'b'); // 无法静态分析
        
      • ESM 优化空间

        // webpack利用静态分析实现的特性
        import(/* webpackPrefetch: true */ './chart'); // 预取
        import(/* webpackChunkName: "utils" */ './utils'); // 分块命名
        

    🌐 浏览器支持情况

    浏览器ESM支持版本
    Chrome61+
    Firefox60+
    Safari10.1+
    Edge16+
    
    

    💡 选用建议

    1. Node.js 服务端

      • 新项目 > Node 14:优先使用ESM
      • 旧项目迁移:逐步替换关键模块
      • 前端工程

        • 统一使用ESM(配合webpack等打包工具)
        • 第三方库需提供ESM版本(通过package.json的module字段)
        • 工具库开发

          # 推荐双模式发布
          lib/
          ├── esm/       # ESM版本(支持Tree-shaking)
          ├── cjs/       # CommonJS版本 
          └── index.d.ts # 类型声明
          

    两种模块系统在JavaScript生态中仍将长期共存,理解其底层机制有助于更高效地处理模块化问题。随着Node.js对ESM支持的完善,未来ESM会成为主流选择,但CommonJS仍将在老项目中持续存在。

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码