在上一篇文章中,我们聊到了”史前时代”js是如何”模拟”模块化的,但本质上依旧是用“旁门左道”来实现的,并不是js语言层面的规范,要想真正实现模块化,肯定需要js自己本身去推出模块化相关的规范,因此这篇文章就准备聊聊这些规范,废话不多说,开搞!
Commonjs
Nodejs 使用的就是Commonjs规范,主要通过提供module,exports,require来实现模块化,require方能看到的只有module.exports这个对象,它是看不到exports对象的,而我们在编写模块时用到的exports对象实际上只是对module.exports的引用,通常情况下更建议使用 module.exports 而不是 exports,该规范的用法如下:
1 | //a.js |
这种规范具有如下几个特点
- 一个模块就是一个对象,是在 运行时 加载的,因此无法进行 静态分析
- 由于无法进行静态分析,因此导出的内容是 全量 的,无法进行 tree-shaking
- 加载模块是 同步的,因此只适合 服务端。因为服务端的资源都在本地,因此读取模块资源的速度很快,但是浏览器环境下资源都是 远程获取 的,因此不适合浏览器环境
- 模块的导出是值拷贝的方式,意思就是变量的变化无法影响外部模块
AMD
AMD规范的主要践行者是 Requirejs,它主要通过 define,require来实现模块化,具体用法如下
1 | /** 网页中引入require.js及main.js **/ |
该模块规范的特点如下:
- 模块的导入是 异步的,因此适用于浏览器环境
- 依赖的模块无法按需导入,只能一次性全部导入,也就是所谓的“依赖前置”
CMD
CMD吸取了AMD和cjs的优点,因此是一种相对前两者更好的模块化方案,Seajs 是该规范的主要践行者,具体用法如下
1 | //定义没有依赖的模块 |
该模块规范有如下特点:
- 由于模块的导入同时支持同步和异步两种方式,因此服务端和浏览器都适用
- 支持模块的 按需导入,只有在真正需要使用依赖模块时才会去导入,也就是所谓的“就近依赖”,我认为这也是更合理的方案
ES module
ES module是 ES6 中提出来的模块化方案,旨在统一浏览器和服务器的模块化规范,也是直接从语言层面来规范js的模块化,所以未来ES module肯定是最终发展的方向,它的具体用法如下
1 | /*a.js*/ |
该规范有如下特点
- 模块的导出只是一个 静态的接口定义,因此在静态分析阶段就能确定模块之间的依赖关系
- 正是由于可以在静态分析阶段就能确定模块之间的依赖关系,所以天然支持tree-shaking
- 因为导出的是静态接口的定义,因此看作导出的是变量的 引用,因此模块内部变量的变化会影响外部的引用,这也是跟cjs存在差异的地方
- 由于在编译期就会导入模块的代码,因此不存在同步或者异步导入模块的说法
- 未来会成为浏览器和服务器的通用模块化方案
结语
未来是 ES module 的天下,只不过现在还处在一个过渡的时期,因此有必要去了解除ES module外的规范,这样我们才能对js这门语言有更加深刻的认识,从而成为一个专业的前端工程师,所以,加油吧,骚年!