前端模块化:CommonJS与ESModule的对比

161 阅读3分钟

1. 为什么会出现 commonjsESModule

早期 JavaScript 模块这一概念,都是通过 script 标签引入 js 文件代码。当然这些基本简单需求没有什么问题,但当我们的项目越来越庞大时,我们引入的 js 文件就会越多,这时就会出现以下问题:

  • js 文件作用域都是顶层,这会造成变量污染
  • js 文件多,变得不好维护
  • js 文件依赖问题,稍微不注意顺序引入错,代码全报错

为了解决以上问题 JavaScript 社区出现了 CommonJsCommonJs 是一种模块化的规范,包括现在的 NodeJs 里面也采用了部分 CommonJs 语法在里面。那么在后来 ES6 版本正式加入了 ESModule 模块,这两种都是解决上面问题:

  • 解决变量污染问题,每个文件都是独立的作用域,所以不存在变量污染
  • 解决代码维护问题,一个文件里代码非常清晰
  • 解决文件依赖问题,一个文件里可以清楚的看到依赖了那些其它文件

2. commonjsESModule 对比

commonjs

  • CommonJs 可以动态加载语句,代码发生在运行时
  • 导出使用 exports 或者 module.exports 。使用 exports 导出之后就不能再导出一个对象值,导出也无效。
  • commonJS 的导出值是拷贝,可以修改。

commonJs 解决上面的 变量污染 和 文件依赖 问题,并且不可以重复导入。

ESModule

  • ESModule 是静态的,只能声明在文件的最上面。
  • 使用 export 或者 export default 混合使用
  • 导出值是映射关系 ,值是只读的不能修改。

ESModule 也 解决上面的 变量污染 和 文件依赖 问题,并且更灵活,导出的值是引用,是可读状态,增强代码的可读性性。

3. importrequire 的对比

1. import 静态使用

import 指令会被 js 引擎静态分析,会先于模块内的其它语句执行。

// 所以下面的代码会报错
// Error
if(condition){
  import module from './module'
 }

⚠️ 注意:引擎处理 import语句是在编译阶段,这时并不会去分析或执行 if 语句,所以 import 语句放在 if 代码块之中毫无意义,因此会报句法错误,而不是执行时错误。也就是说,importexport 命令只能在模块的顶层,不能在代码块之中。

2. import 的动态使用

import 作为静态资源加载的话,有利于提高编译的效率。但是却无法实现在运行时加载模块。就无法实现条件加载,要用 import 取代 Node 的 require() 的话就行不通,因为 require() 是在运行时加载模块,import 无法实现动态加载。比如:

// 动态加载,只有在运行的时候才知道加载的是什么
const path = './' + fileName;
const module = require(path);

于是有人提案,引入 import() ,实现动态加载。

import(module)

import() 返回一个 promise 对象。可以结合 Promise 使用。

3.import import() 的区别

import() 函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。另外,import() 函数与所加载的模块没有静态连接关系,这点也是与 import 语句不相同。

4.import() 和 require() 的区别

import()异步加载require()同步加载