前端入门22-讲讲模块化

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 命令的工作原理:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzgpj.html