浅谈前端模块化

370 阅读7分钟

这里的浅谈,真的只是很肤浅的谈一谈,
本文甚至不会有关于各种模块化的语法、使用等。
就像张鑫旭老师讲promise的一篇文章:《ES6 JavaScript Promise的感性认知》一样,
仅仅希冀这篇文章能帮我建立起一个对模块化的初步印象,一个好印象,以便后面能更有兴趣去深入学习。
所以仅仅是我个人的碎碎念罢了。

——————————

看了几篇文章,对模块化的几种方式都有一些初步认识,
IIFE(自执行函数)、AMD规范的实现RequireJS、CMD的实现SeaJS、ES6 Module……

为什么会发展出这么多种模块化?
这么多模块化在实际应用中,都有什么利弊?
先前的模块化方式为什么会被淘汰?

这几个名词在各路大佬的博客里反复出现,但看来看去好像只是死板的文字出现在屏幕上,
它是它我是我,我不认识它它也不认识我。
如果把它比作一个人,那现在我根本没有跟他聊下去的欲望。

认识一个人,了解一个人,应该从他的故事出发,知道他身上的经历,才能逐渐建立一个丰满的人像。
认识模块化,也应从历史出发,看它怎么一步步发展至今。

以“模块化 发展”为关键词,我在掘金搜索到这样一篇文章:《JavaScript 模块化发展历程》。 以下内容则是在此文章基础上写的

模块化发展之一

【无模块化】

最开始没有人写模块化,但是维护过于麻烦:

  • 1⃣️ - 模块间的依赖不清晰
  • 2⃣️ - 还要提防全局变量的冲突
  • 3⃣️ - 手动维护各个<script>标签的顺序 这太令人烦躁了。

【命名空间】

后来为了解决全局变量的冲突问题,同时为了让模块间依赖更加清晰,大家开始使用将函数存储在对象(对象可以与模块【一个JS文件就是一个模块】同名),一个模块返回一个对象,然后外部调用对象中的属性时 就是调用该函数。
这种方法虽然一定程度上缓解了无模块化的问题: 1⃣️ - 模块间的依赖不清晰、3⃣️ - 全局变量的冲突。
但仅仅是缓解,并没有解决。
而且仍然存在自己的弊端
模块中的方法被暴露出去,也会被人更改,不安全。

【IIFE(立即执行)】

之后就是使用IIFE来操作模块化,将【命名空间】中存储函数的对象放在闭包中,将此对象作为返回值,返给一个变量。
至此,只能通过变量调用对象的方法,而对象保存在闭包中,无法修改,外部只能调用闭包中暴露出来的方法(或许也可以称之为接口)。

关于

上面三个阶段,我自己愿意称之为第一个阶段,模块化并没有一个相关的规范,模块的维护仍然有很大的阻力。

模块化发展之二

参考《JavaScript 模块化发展历程》及其参考链接中玉伯的《前端模块化开发那点历史》

ServerJS --> CommonJS --> AMD(Require.js) --> CMD(Sea.js)

大概 09 年 - 10 年期间,CommonJS 社区大牛云集。CommonJS 原来叫 ServerJS,推出 Modules/1.0 规范后,在 Node.js 等环境下取得了很不错的实践。

09年下半年这帮充满干劲的小伙子们想把 ServerJS 的成功经验进一步推广到浏览器端,于是将社区改名叫 CommonJS

ServerJSModules/1.0规范的具体实现,
Node.js等环境下取得了成功,于是希望扩展到浏览器环境下,
同时将名字更名为CommonJS,以此希冀其适用性更加普遍。

规范实现
Modules/1.0ServerJS / CommonJS
AMDRequire.js
CMDSea.js

从一个环境扩展到另一个环境,必然引起规范的重新制定(扩展)。

09年下半年这帮充满干劲的小伙子们想把 ServerJS 的成功经验进一步推广到浏览器端,于是将社区改名叫 CommonJS,
同时激烈争论 Modules 的下一版规范。分歧和冲突由此诞生,逐步形成了三大流派:

  • 第一种流派认为,现有规范就OK,直接将代码转译成浏览器可识别的即可。玉伯的文章是十多年前的了,他举了两个当时流行的例子,放在时下,可能跟babel.js有异曲同工之妙。,我称之为【转译派】
  • 第二种流派认为,浏览器跟服务器不一样,有自己的特性,应该重新制定规范。这个观点下的典型代表是AMD 规范及其实现 RequireJS。我称之为【重置派】
  • 第三种流派认为,规范应该重新制定,但是应该跟1.0差不多。我称之为【限制重置派】

第三种流派的实现有 BravoJS 和 FlyScript 。
BravoJS 的作者提出Modules/2.0-draft规范,过于学院派。
FlyScript 的作者提出Modules/Wrappings规范,但是后来将网站下架。

ok,话题拽回来,这三种流派当时讨论激烈,其中以第二种流派AMD规范及其实现Require.js最为受欢迎,“AMD 的流行,很大程度上取决于 RequireJS 作者的推广”

第二种流派的Require.js是个实战派,
在它面前,第三种流派的BravoJS太学院派,不堪一击
不过后来,第三种流派又出了个实战派的FlyScript

FlyScript 抛去了 Modules/2.0 中的学究气,提出了非常简洁的 Modules/Wrappings 规范

悲催的是,FlyScript 在推出正式版和官网之后,RequireJS 当时正直红火。期间 FlyScript 作者 khs4473 和 RequireJS 作者 James Burke 有过一些争论。再后来,FlyScript 作者做了自我阉割,将 GitHub 上的项目和官网都清空了

如此种种,不必多言。

玉伯 在不断给 RequireJS 提建议,但不断不被采纳后,开始萌生了自己写一个 loader 的念头。
这就是 Sea.js
Sea.js 借鉴了 RequireJS 的不少东西,比如将 FlyScript 中的 module.declare 改名为 define 等。
Sea.js 更多地来自 Modules/2.0 的观点,但尽可能去掉了学院派的东西,加入了不少实战派的理念。

发展史

激烈的争论、规范地提出、实现,诸如此类,看这样的发展历程不免觉得江湖意气浓重,
这样的江湖气反而让模块化的形象更加丰满

应该说万事万物都有存在的道理,也有其消亡的道理,可能是硬实力不够,也可能仅仅是时机没有把握到。

观上文

【命名空间】、【IIFE】更像是修仙文中的散修,虽然在修仙大道上,但十分艰苦。
【CommonJS】更像是各类宗门的发源地,在蓬莱仙岛发展不错后,就想去中心大陆开枝散叶,可到了中心大陆,难免需要因地制宜,于是宗门内部发展出了三个流派:【1-转译派】、【2-重置派】、【3-限制重置派】
三个流派各自发展,最终脱离【CommonJS】自行发展。

【2-重置派】创立了心法【AMD规范】,而后又创立了配套功法【Require.js】。

相较于【2-重置派】【3-限制重置派】的发展可谓是历经坎坷,
【Modules/2.0-draft】心法下的功法【BravoJS】太学院派,
后来又有人创立【Modules/Wrappings】心法以及【FlyScript】功法,
虽然实战性增强了,但此时【2-重置派】的【Require.js】如日中天,
两个创立者之间的论道也以【3-限制重置派】败北告终。

玉伯可能比较推崇【3-限制重置派】,就算一直用着【2-重置派】的功法【Require.js】,心里也对【3-限制重置派】的心法念念不忘。
天不绝此派,玉伯最终在之前【3-限制重置派】的基础上创立了【CMD】心法以及【Sea.js】功法。

这么一讲,真的是江湖意气。心潮澎湃,挺有意思也是。