JS模块化

3 阅读3分钟

js模块发展的基本过程:

早期 -> CommonJs -> AMD -> UMD -> ESModule

早期

<script src="./module1.js"></script>
<script src="./module2.js"></script>
<script src="./module3.js"></script>

早期js没有模块化, 只是将不同代码块写在不同的js文件中进行简单的隔离.

问题:

  • 所有js文件中的变量依然处于同一个全局作用域下, 就会有变量提升, 重复定义的问题
  • 代码块之间有依赖关系的话,需要额外关注脚本加载的顺序

为解决上面的问题, 需要将脚本文件进行[模块化]:

  • 每个模块在自己的变量作用域, 避免污染全局作用域和避免与其他模块的代码发生冲突
  • 模块之间有互相导入和导出的通信方法
  • 模块的加载与执行遵循一定的规范 (模块导入单例化), 保证模块之间的依赖关系.

CommonJs

CommonJs是Nodejs实现的模块化规范.

在CommonJS 规范里,每个 JS 文件就是一个 模块(module),每个模块内部可以使用require函数和 module.exports对象来对模块进行导入和导出。

//index.js
require("./moduleA");
var m = require("./moduleB");
console.log(m);

//moduleA.js
var m = require("./moduleB");
setTimeout(()=>console.log(m),1000);

//moduleB.js
var m = new Date().getTime();
module.exports m;

node 才会在解析 JS 的过程中提供⼀个 require ⽅法,这样当解析器执⾏代码时,发现有模块调⽤了 require 函数,就会通过参数找到对应模块的物理路径,通过系统调⽤从硬盘读取⽂件内容,解析这段内容最终拿到导出结果并返回。

从它的执⾏过程也能看出来 CommonJS 是⼀个 同步加载模块 的模块化规范,每当⼀个模块 require ⼀ 个⼦模块时,都会停⽌当前模块的解析直到⼦模块读取解析并加载。

AMD

受到 CommonJS 模块化规范的启发,WEB 端逐渐发展出 异步模块定义 AMD (Asynchronous module definition).

简单理解, AMD规范 约等于 CommonJs + 异步模块加载.

不同于CommonJs的同步加载模块, AMD应用场景是web开发, 为避免模块同步加载阻塞页面响应的问题, 所以AMD模块默认都是异步加载.

<script src="/require.js"></script>
// index.js
require(['moduleA', 'moduleB'], function (moduleA, moduleB) {
    console.log(moduleB);
});
// moduleA.js
define(function (require) {
    var m = require('moduleB');
    setTimeout(() => console.log(m), 1000);
});
// moduleB.js
define(function (require) {
    var m = new Date().getTime();
    return m;
});

UMD

UMD(Universal Module Definition) 作为⼀种同构(强调在不同环境中运行时的不变性)的模块化解决方案出现,它能够让我们只需要在⼀个地⽅定义模块内容,并同时兼容 AMD 和 CommonJS 语法。

检测出当前的模块化规范, 然后将模块内容按对应的模块化规范语法导出即可

(function (self, factory) {
 if (typeof module == 'object' && typeof module.exports == 'object') {
        //当前环境是CommonJS规范环境
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        //当前环境是AMD规范环境
        define(factory)
    } else {
        //什么环境都不是,直接挂在全局对象上
        self.umdModule factory();
    }
}(this, function () {
    return function () {
        return Math.random();
    }
}));

ESModule

CommonJs/AMD 有以下特点:

  • 语言上层的运行环境中实现的模块化规范
  • 相互之间不能共享模块, AMD模块不能被用于node环境, CommonJs模块不能被用于浏览器环境

ES6 之后,JS 有了语⾔层⾯的模块化导入导出关键词与语法以及与之匹配的 ESModule 规范.

ESModule 与 CommonJS 和 AMD 最⼤的区别在于,ESModule 是由 JS 解释器实现,⽽后两者是在宿主环境中运⾏时实现。ESModule 导⼊实际上是在语法层⾯新增了⼀个语句,⽽ AMD 和 CommonJS 加载模块 实际上是调⽤了 require 函数。

// index.js
import './moduleA';
import m from './moduleB';
console.log(m);

// moduleA.js
import m from './moduleB';
setTimeout(()) => console.log(m), 1000);

// moduleB.js
var m = new Date().getTime();
export default m;