js模块化已经是一个老生常谈的问题了,关于js模块化的文章也非常的多,很多文章都清晰的描述了amd,cmd,umd,commonjs之间的区别。但是因为模块化问题被频频讨论所以有几个点不是很清楚,在此请教各位大佬
1、es6+babel/commonjs已经能解决大部分问题了,详细了解amd和cmd和umd是为了解决webpack1这种的落后框架才需要继续去了解其本质区别嘛? 而且事实上现在有许多的框架可以方便的帮我们处理其模块化方式,为什么模块化的问题还是被频频讨论。或者说如果不了解amd和cmd这种的本质区别有什么问题是在开发过程中无法处理的,还或者es6+babel无法适用于哪些场景?
2、尽管大部分资料都能告诉我们amd和cmd一个就近依赖一个依赖置顶,一个requirejs推出的一个是seajs推出的、但是很少有资料能通透的讲出这些模块化规范的本质区别,请问这些规范的本质区别是什么?
3、这些规范当中哪些是同步哪些是异步?
ESM 目前已经在用的 import/export 是静态加载,可以看作是同步的,因为它要加载完成之后才会执行下面的脚本。不过还有一个
import()
用于动态加载,它返回一个 Promise 对象,是异步的。构建工具可以根据配置把 ESM 转译成上述各种规范,所以……学正室就好。
还好,最后说明一下,同步(静态)/异步(动态)的本质区别:
同步改异步只需要加 Promise 封装即可;
异步改同步使用
await
关键字,参考:理解 JavaScript 的 async/await来看下 RequireJS 的模块定义语法,这是官方示例和解释:
就是说,
helper/util
加载完成之后才会调用function(util) {...}
,而模块加载。这里function(util)
是当前模块的工厂函数。这个加载过程大概是这样的:helper/util
在缓存中,直接算加载完成,将缓存中的helper/util
模块导出项作为当前模块工厂函数的util
参数传入helper/util
不在缓存中,等待加载。加载helper/util
的过程中如果需要加载其他依赖项,那就继续递归等待,所有依赖加载完成,helper/util
加载成功,缓存其导出项并将其导出项作为util
参数传入当前模块工厂所以,这个过程是异步的,也就是说 AMD 是异步的。调用
requirejs
的时候,是异步加载依赖项,完成之后调用后面的回调(工厂函数)接着看 SeaJS,
define
直接定义了一个工厂函数,其中require
是作为一个参数传进来的。注意到这句:require
并没有等待加载 jquery 完成的过程(没有回调,也没有await
这种典型的异步特征),就将结果赋值给$
了,这说明 jquery 早就已经加载进来了。那 jquery 是什么时候加载进来的呢?注意到下面主程序中的代码:这是一个典型的异步形式。所以 SeaJS 是在
use
的时候一次性分析完所有依赖模块,分析他们的依赖关系,做好缓存之后再来执行的主程序。所以 CMD 的模块工厂是同步的,但是最上层的use
是异步的(浏览器 Ajax 只能异步加载,不异步也不行啊)CommonJS,这个不用官方的例子,随便都能写两个
这个也很明显,
require()
是典型的同步调用(没有回调,也没有await
等),所以 CJS 模块也是同步的。因为 Node 有自己的 Runtime,它完全可以做到 SeaJS 做不到的事情,把加载过程做成完全的同步过程。ESM 的 import 其实也是同步的,浏览器在自己控制范围内是也可以同步加载的,比如原始的
<script>
就是阻塞的,所以import
可以同样实现。Node 就不用说了,一个道理。只能说自己能控制运行时真好,可怜的 SeaJS。示例不用写了,跟 CommonJS 的那个道理一样,就当是不同的语法干同样的事情吧。另外还有一个
import()
,是这样用的或者
这是典型的异步加载。所以 EJS 默认支持同步加载模块,通过
import()
提供异步(动态)加载能力。