JavaScript模块化开发的演进历程
感谢各位大佬的分享,解惑了很多。
正文-模块化现在回过头来想想,也许选择以《JavaScript权威指南》一书来作为入门有些不好,因为这本书毕竟是很早之前的,书中所讲的思想、标准也基本都只是 ES5 及那时代的相关技术。
这也就导致了,在书中看到的很多例子,虽然觉得所用到的思想很奇妙,比如临时命名空间之类的处理,但其实,有些技术到现在已经有了更为强大的技术替代了。
就像这篇要讲的模块化,目前,以我看到的各资料后,所收获的知识是,大概有四种较为常用且热门的模块化技术,也许还有更新的技术,也许还有我不知道的技术,无所谓,慢慢来,这篇的内容已经够我消化了。
目前四种模块化技术:
CommonJS规范&node.js
AMD规范&Require.js
CMD规范&Sea.js
ES6标准
前面是规范,规范就是类似于 ES5,ES6 只是提出来作为一种标准规范,而不同规范有具体的实现,比如 nodeJS 实现了 CommonJS 规范。
模块化历程在声明部分中的第二、第三链接里那两篇,以时间线介绍了模块化的相关技术的发展历程,我觉得很有必要一看,对于掌握和理解目前的模块化技术挺有帮助的。
这里,就稍微摘抄其中一些内容,详细内容还是需要到原文阅读。
全局变量、全局函数(1999年)
这时候的多个 js 脚本文件之间,直接通过全局变量或全局函数的方式进行通信,这种方式叫做:直接定义依赖。
虽然做的好一些的会对这些 js 文件做一些目录规划,将资源归类整理,但仍无法解决全局命名空间被大量污染,极其容易导致变量冲突问题。
对象作为命名空间(2002年)
为了解决遍地的全局变量问题,这时候提出一种命名空间模式的思路,即将本要定义成全局变量、全局函数的这些全都作为一个对象的属性存在,这个对象的角色充当命名空间,不同模块的 JS 文件中通过访问这个对象的属性来进行通信。
立即执行的函数作为临时命名空间 + 闭包(2003年)
虽然提出利用一个对象来作为命名空间的思路,一定程度解决了大量的全局变量的问题,但仍旧存在很多局限,比如没有模块的隐藏性,所以针对这些问题,这时候又新提出一种思路:利用立即执行的函数来作为临时命名空间,这样就可以避免污染全局命名空间,同时,结合闭包特性,来实现隐藏细节,只对外暴露指定接口。
虽然这种思路,解决了很多问题,但仍旧有一些局限,比如,缺乏管理者,什么意思,就是说,在前端里,开发人员得手动将不同 JS 脚本文件按照它们之间的依赖关系,以被依赖在前面的顺序来手动书写 <script> 加载这些文件。
也就是不同 <script> 的前后顺序实际上表示着它们之间的依赖关系,一旦文件过多,将会很难维护,这是上述方案都存在的问题。
动态创建 <script> 工具(2009年)
针对上述问题,也就衍生出了一些加载 js 文件的工具,先看个例子:
$LAB.script("greeting.js").wait() .script("x.js") .script("y.js").wait() .script("run.js");LAB.js 这类加载工具的原理实际上是动态的创建 <script>,达到作为不同 JS 脚本文件的管理者作用,来决定 JS 文件的加载和执行顺序。
虽然我没用过这种工具,但我觉得它的局限还是有很多,其实就是将开发人员本来需要手动书写在 HTML 文档里的 <script> 代码换成写在 JS 文件中,不同 JS 文件之间的依赖关系仍旧需要按照前后顺序手动维护。
CommonJS规范&node.js(2009年)
中间跳过了一些过程,比如 YUI 的沙箱模式等,因为不熟,想了解更详细的可以去原文阅读。
当 CommonJS 规范出来时,模块化算是进入了真正的革命,因为在此之前的探索,都是基于语言层面的优化,也就是利用函数特性、对象特性等来在运行期间模拟出模块的作用,而从这个时候起,模块化的探索就大量的使用了预编译。
CommonJS 模块化规范有如下特点:
所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序。
不同的模块间的依赖关系通过 require 来控制,而每个模块需要对外暴露的接口通过 exports 来决定。
由于 CommonJS 规范本身就只是为了服务端的 node.js 而考虑的,node.js 实现了 CommonJS 规范,所以运行在 node.js 环境中的 js 代码可以使用 require 和 exports 这两个命令,但在前端里,浏览器的 js 执行引擎并不认识 require 这些命令,所以需要进行一次转换工作,后续介绍。
再来看看 require 命令的工作原理: