blog tumbnail

CommonJs 和 Es Modules 的不同和用法

CommonJs和EsModule的由来

在早期 JavaScript 的使用过程中,都是通过 script 标签来引入 js 代码。当 js 代码引入过多时,就会发生以下几个问题:

  • 变量污染问题,因为每个文件都不存在独立的作用域,使用不同文件定义的变量存在相互污染的问题
  • 文件依赖问题,因为文件依赖不明确,无法确定具体依赖哪些文件
  • 代码组织不清晰,不易于维护

CommonJsEsModules 就是为了解决以上问题而诞生的。

CommonJs

CommonJS 是一组用于在 JavaScript 上实现模块的标准。该项目由 Mozilla 工程师 Kevin Dangoor 于2009年启动。CommonJS 主要应用于 node 应用,浏览器不支持使用 CommonJS。而之后 webpack 的出现,让模块化也运用到了前端。

EsModules

ES6 开始,JavaScript 支持称为 ES ModuleECMAScript Modules 的模块格式。这是 JavaScript 支持模块化的现代方法。 目前有以下环境支持:

  • Node.js - Node 12 开始
  • Deno
  • 浏览器 - 包括 Chrome,Firefox 和 Safari 从2018年开始支持 Es Modules

CommonJs基本语法

CommonJs 模块规范来说,每个文件就是一个模块,有自己的作用域。在这个文件内定义的变量、函数、类,都是私有的,对其他文件不可见。

导出

CommonJs 中使用 module.exports 导出变量函数等

// 导出任意值 module.exports.num = 11 module.exports.words = 'Hello World!' module.exports.isShow = true // 也可以写成 exports exports.num = 11 exports.words = 'Hello World!' exports.isShow = true
// 导出对象 module.exports = { num:11, words:'Hello World!', isShow:true } // 也可以写成 exports exports = { num:11, words:'Hello World!', isShow:true }

注意:exports 是 node 为了方便,在每个模块提供了一个 exports 变量,指向 module.exports。所以不能直接将 exports 指向单个值。具体可看 CommonJS规范

// 无效 exports = function(x) {console.log(x)};
// 无效 exports.hello = function() { return 'hello'; }; module.exports = 'Hello world';

为了不增加心智负担,还是不推荐使用 exports ,只使用 module.exports

导入

CommonJs 中使用 require 语法进行导入,即读入并执行一个 JavaScript 文件,然后返回该模块的 exports 对象。如果没有发现指定模块,则会报错。

// work.js module.exports.num = 11 module.exports.words = 'Hello World!' module.exports.isShow = true // index.js let work = require('./work.js') console.log(work) // { num:11,words:'Hello World!',isShow:true } let num = require('./work.js').num console.log(num) // 11

CommonJs 支持动态导入,即在执行语句中间进行导入。

if(a > 0){ let b = require('./work.js') }

这个时候 b 输出的是 a 输出值的拷贝。这个时候,模块内部的变化就影响不到 b 的值。同时,b 值也可以在后续的使用中修改或覆盖。这里需要注意防止变量污染。

EsModule基本语法

导出

EsModules 导出分为两种,单个导出(export)默认导出(export default)单个导出commonJs 不同,commonJs 是导出的全部值都一起导入,而 EsModules 是可以选择导入的值。默认导出(export default)则是全部一起导入。

// 单个导出,可导出多个 export const num = 11 export const words = 'Hello World!'
// 默认导出 export default { say() { console.log('Yeah!') }, num: 11 }

单个导出默认导出可混合使用,但需要注意导入时,需要先导入默认导出,再导入单个导出

// dog.js export const owner = 'xiaoA' export default { name: 'puppy', say(){ console.log('汪汪') } } // index.js import dog, { owner } from './dog.js' console.log(owner) // xiaoA console.log(dog.name) // puppy dog.say() // 汪汪

导入

EsModules 使用 import 语法进行导入,单个导入需要使用花括号导入 { }

// 单个导出 // a.js export const num = 11 export const words = 'Hello World!' import { num, words } from './a.js' console.log(num) // 11 console.log(words) // Hello World! // 如果都是单个导出,可以使用以下方法全部导入 import * as a from './a.js' console.log(a) // { num:11, words: 'Hello World!'}

export 导出导入是值的引用,这点与 commonJs 不同,引入的值是只读状态,不能进行修改。 同时,EsModule 只能在文件的最顶端导入,不能动态引入。如果动态引入则会报错。

if(a > 0){ import b from './b.js' // 报错 }

CommonJs和EsModule的不同

CommonJsEsModules 都是为了将 JavaScript 程序拆分为可单独导入的模块机制。不过它们在使用上还是有所不同。

  • EsModules 主流浏览器已基本兼容,而 CommonJs 则需要搭配 webpack 等工具才能上浏览器
  • CommonJs 可以在代码执行时动态加载,而 EsModules 不可以动态加载,必须声明在文件顶部
  • CommonJs 导出值是拷贝值,可进行修改,而 EsModules 是引用值,不能进行修改