「Node.js」ESModule 与 CommonJS 的 区别

2024-05-05 02:52
文章标签 js 区别 node commonjs esmodule

本文主要是介绍「Node.js」ESModule 与 CommonJS 的 区别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

Node.js支持两种模块系统:CommonJS 和 ESModules(ESM),它们在语法和功能上有一些不同。
在这里插入图片描述

CommonJS (CJS)

CommonJS 是 Node.js 最早支持的模块规范,由于它的出现在ES6之前,因此采取的是同步加载模块的方式。这在服务端是可接受的,因为文件都在本地,同步加载不会引起明显的延迟问题。但是这样的加载方式不适合用在客户端,因为它会导致浏览器在等待模块下载和解析的期间挂起。

  • CommonJS 通过 require 函数来导入模块,如 const module = require('module_name')
  • 模块导出使用 module.exports 对象,如 module.exports = valueexports.value = value

ESModules (ESM)

ESModules 是ES6引入的官方标准化模块系统,支持静态导入和导出。静态模块结构允许JavaScript引擎优化加载和解析。

  • 使用 import 关键字导入模块,例如 import { something } from 'module_name'
  • 使用 export 关键字导出模块,例如 export function doSomething() { ... }export default class MyClass{ ... }

ESModules设计为可以异步加载,这使其更适合于用在浏览器环境中,不过Node.js现在也支持ESModules。

区别

  1. 加载机制:

    • CommonJS:同步加载。
    • ESModules:可以异步加载,支持静态分析,预测性地进行代码的分割和按需加载。
  2. 模块解析:

    • CommonJS 中,require 路径解析算法基于文件系统运作,并且可以省略文件后缀或指向文件夹。
    • ESModules 使用更为严格的路径,通常需要文件后缀,遵循 URL 语法。
  3. 导出值:

    • CommonJS 导出的是值的拷贝,也就是说,一旦 require() 执行完毕,模块内部的变化不会影响这个值。
    • ESModules 导出的是绑定的引用,模块外部能够实时观察到模块内部的变化。
  4. 模块上下文:

    • CommonJS 每个文件都是其模块的顶层作用域。
    • ESModules 顶层 thisundefined

应用场景

CommonJS 和 ESModules 由于它们各自的特点,在不同的应用场景下会有所偏好。下面列举了它们的典型应用场景:

CommonJS 的应用场景:
  • Node.js服务端开发:由于服务端代码不需要考虑浏览器端的下载和延迟问题,CommonJS 的同步加载特性在服务端是完全可行的。同时,绝大多数Node.js的生态系统(npm上的包)都是基于CommonJS规范的。
  • 旧项目维护:早期的Node.js项目都是基于CommonJS规范的,所以维护旧项目往往意味着继续使用CommonJS。
  • 需要动态计算或条件加载模块的情况:由于CommonJS 支持运行时同步加载,你可以根据某些运行时计算的条件来要求模块。
ESModules 的应用场景:
  • 现代化前端开发:如使用框架(例如React、Vue、Angular)和工具(如Webpack、Rollup、Parcel)构建项目。ESModules 支持动态导入(import())和静态代码分析,这有利于前端工具进行代码拆分,以懒加载的方式优化应用加载性能。
  • 跨环境的代码共享:比如同时在Node.js和现代浏览器中运行的库或工具。ESModules是JavaScript官方的模块系统,现代浏览器原生支持。
  • 新项目开发:创建新的Node.js项目时,倾向于使用ESModules,因为它是ECMAScript规范的一部分,并且Node.js及其生态系统正在向ESModules过渡。
  • 需要利用静态分析来进行Tree-shaking优化的项目:Tree-shaking是一种只打包必要模块代码的方法,它依赖于ESModules的静态结构。
混合场景:
  • 迁移的项目:如果你的项目计划从CommonJS迁移到ESModules,你可能会临时进入混合状态,这时要注意不同模块系统间的互操作。
  • 使用第三方库:当需要在ESModules项目中使用仅提供CommonJS模块的第三方库时,也会出现混合使用。

Node.js已经提供了某些语法糖和特性来平滑CommonJS到ESModules的过渡,如import()动态导入语法来异步加载CommonJS模块等。

选择使用CommonJS还是ESModules很大程度上依赖于项目的目标环境、工具链支持、代码库的现状以及个人或团队的偏好。随着技术演进,社区正在向ESModules靠拢,但在稳定性、交付和兼容性要求很高的情况下,CommonJS仍然是一个合理而且可靠的选择。

使用示例

CommonJS

导出示例:

// myModule.js
const myFunction = () => {console.log('Hello, CommonJS!');
}
// 给exports对象添加属性
exports.myFunction = myFunction;

导入示例:

// app.js
const myModule = require('./myModule.js');
myModule.myFunction(); // 输出: 'Hello, CommonJS!'
ESModules

导出示例:

// myModule.js
export const myFunction = () => {console.log('Hello, ESModules!');
}

导入示例:

// app.js
import { myFunction } from './myModule.js';
myFunction(); // 输出: 'Hello, ESModules!'

此外,下面的示例说明了CommonJS如何在运行时加载模块,而ESModules则在编译时加载模块。

CommonJS 运行时加载
// 假设我们在运行代码之前并不知道我们需要加载的模块版本,我们可以在运行时动态地确定。
let version = 'v1';
if (someCondition) {version = 'v2';
}
const module = require(`./module${version}.js`);
ESModules 编译时加载
// 在ESModules中,不能这样动态加载模块,因为所有的import都会在编译时被处理。
// 下面的代码无法运行,会提示错误。
let version = 'v1';
if (someCondition) {version = 'v2';
}
// 报错
import module from `./module${version}.js`;
ESModules 动态加载

但是,ESModules 提供了一种动态导入的方法,可以在运行时异步加载模块:

let version = 'v1';
if (someCondition) {version = 'v2';
}
import(`./module${version}.js`) .then(module => {// 使用模块}).catch(err => {// 处理加载错误});

在这个例子中,import() 返回一个 Promise,加载完模块后会被解析,因此动态导入必须在 Promise 或 async/await 上下文中处理。

这篇关于「Node.js」ESModule 与 CommonJS 的 区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/960665

相关文章

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu

Three.js构建一个 3D 商品展示空间完整实战项目

《Three.js构建一个3D商品展示空间完整实战项目》Three.js是一个强大的JavaScript库,专用于在Web浏览器中创建3D图形,:本文主要介绍Three.js构建一个3D商品展... 目录引言项目核心技术1. 项目架构与资源组织2. 多模型切换、交互热点绑定3. 移动端适配与帧率优化4. 可

Go之errors.New和fmt.Errorf 的区别小结

《Go之errors.New和fmt.Errorf的区别小结》本文主要介绍了Go之errors.New和fmt.Errorf的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考... 目录error的基本用法1. 获取错误信息2. 在条件判断中使用基本区别1.函数签名2.使用场景详细对

Redis中哨兵机制和集群的区别及说明

《Redis中哨兵机制和集群的区别及说明》Redis哨兵通过主从复制实现高可用,适用于中小规模数据;集群采用分布式分片,支持动态扩展,适合大规模数据,哨兵管理简单但扩展性弱,集群性能更强但架构复杂,根... 目录一、架构设计与节点角色1. 哨兵机制(Sentinel)2. 集群(Cluster)二、数据分片

一文带你迅速搞懂路由器/交换机/光猫三者概念区别

《一文带你迅速搞懂路由器/交换机/光猫三者概念区别》讨论网络设备时,常提及路由器、交换机及光猫等词汇,日常生活、工作中,这些设备至关重要,居家上网、企业内部沟通乃至互联网冲浪皆无法脱离其影响力,本文将... 当谈论网络设备时,我们常常会听到路由器、交换机和光猫这几个名词。它们是构建现代网络基础设施的关键组成

redis和redission分布式锁原理及区别说明

《redis和redission分布式锁原理及区别说明》文章对比了synchronized、乐观锁、Redis分布式锁及Redission锁的原理与区别,指出在集群环境下synchronized失效,... 目录Redis和redission分布式锁原理及区别1、有的同伴想到了synchronized关键字

JAVA覆盖和重写的区别及说明

《JAVA覆盖和重写的区别及说明》非静态方法的覆盖即重写,具有多态性;静态方法无法被覆盖,但可被重写(仅通过类名调用),二者区别在于绑定时机与引用类型关联性... 目录Java覆盖和重写的区别经常听到两种话认真读完上面两份代码JAVA覆盖和重写的区别经常听到两种话1.覆盖=重写。2.静态方法可andro

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

MyBatis中$与#的区别解析

《MyBatis中$与#的区别解析》文章浏览阅读314次,点赞4次,收藏6次。MyBatis使用#{}作为参数占位符时,会创建预处理语句(PreparedStatement),并将参数值作为预处理语句... 目录一、介绍二、sql注入风险实例一、介绍#(井号):MyBATis使用#{}作为参数占位符时,会

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期