JS模块化历史进程

模块化——DOM脚本和语言的分水岭

脚本还是语言?

当 Brendan Eich 设计 JavaScript 的第一个版本时,他可能不知道他的项目在过去 20 年将如何发展。目前,该语言规范已经有六个主要版本,其改进工作仍在继续。

老实说,JavaScript 从来都不是完美的编程语言。 JS 的弱点之一是模块化,更清楚地说,它的缺席。确实,当您仅将脚本语言用于页面上飘落的雪花动画或表单验证时,当一切都可以在同一个全局范围内生活和交互时,为什么您需要关心代码和依赖项的隔离?

随着时间的推移,JavaScript 已经转变为一种通用语言,因为它开始用于在各种环境(浏览器、移动设备、服务器、物联网)中构建复杂的应用程序。通过全局范围交互程序组件的旧方法变得不可靠,因为越来越多的代码往往会使您的应用程序过于脆弱。这就是为什么为了简化创建 JavaScript 应用程序而创建了各种模块化实现。

两个核心问题

  • 命名冲突
    从它出现的那一刻起,JavaScript 就使用全局对象窗口作为没有使用 var 关键字定义的所有变量的存储。 在早期,它非常方便,因为 JavaScript 代码倾向于解决不需要很多代码行的小任务。 但是当应用程序的代码库变得很大时,该语言的这一特性开始由于名称冲突而导致严重的错误。
  • 代码共享
    JavaScript 构建大型应用程序的另一个不便之处是需要在最常见的 ES5 浏览器环境中使用 script 标签显式指定插件脚本。如果您关心应用程序的源代码应该是可维护的,那么您需要将其拆分为独立的部分。 因此,文件的数量可能非常大。 对于大量文件,手动控制脚本(即通过脚本标签将脚本放置在页面上)变得非常繁琐,因为首先您必须记住在页面中放置必要的脚本,其次要保持正确的顺序 脚本标签,以便解决文件之间的所有依赖关系。

JS模块化进程概述

直接定义依赖 (1999): 由于当时 js 文件非常简单,模块化方式非常简单粗暴 —— 通过全局方法定义、引用模块。这种定义方式与现在的 commonjs 非常神似,区别是 commonjs 以文件作为模块,而这种方法可以在任何文件中定义模块,模块不与文件关联。

闭包模块化模式 (2003): 用闭包方式解决了变量污染问题,闭包内返回模块对象,只需对外暴露一个全局变量。

模版依赖定义 (2006): 这时候开始流行后端模版语法,通过后端语法聚合 js 文件,从而实现依赖加载,但挂在可维护性上。

注释依赖定义 (2006): 几乎和模版依赖定义同时出现,与 1999 年方案不同的,不仅仅是模块定义方式,而是终于以文件为单位定义模块了,通过 lazyjs 加载文件,同时读取文件注释,继续递归加载剩下的文件。

外部依赖定义 (2007): 这种定义方式在 cocos2d-js 开发中普遍使用,其核心思想是将依赖抽出单独文件定义,这种方式不利于项目管理,毕竟依赖抽到代码之外,我是不是得两头找呢?所以才有通过 webwpack 打包为一个文件的方式暴力替换为 commonjs 的方式出现。

Sandbox模式 (2009): 这种模块化方式很简单,暴力,将所有模块塞到一个 sanbox 变量中,硬伤是无法解决明明冲突问题,毕竟都塞到一个 sandbox 对象里,而 Sandbox 对象也需要定义在全局,存在被覆盖的风险。模块化需要保证全局变量尽量干净,目前为止的模块化方案都没有很好的做到这一点。

依赖注入 (2009): 就是大家熟知的 angular1.0,依赖注入的思想现在已广泛运用在 react、vue 等流行框架中。但依赖注入和解决模块化问题还差得远。

CommonJS (2009): 真正解决模块化问题,从 node 端逐渐发力到前端,前端需要使用构建工具模拟。

Amd (2009): 都是同一时期的产物,这个方案主要解决前端动态加载依赖,相比 commonJs,体积更小,按需加载。

Umd (2011): 兼容了 CommonJS 与 Amd,其核心思想是,如果在 commonjs 环境(存在 module.exports,不存在 define),将函数执行结果交给 module.exports 实现 Commonjs,否则用 Amd 环境的 define,实现 Amd。

Labeled Modules (2012): 和 Commonjs 很像了,没什么硬伤,但生不逢时,碰上 Commonjs 与 Amd,那只有被人遗忘的份了。

YModules (2013): 既然都出了 Commonjs Amd,文章还列出了此方案,一定有其独到之处。其核心思想在于使用 provide取代 return,可以控制模块结束时机,处理异步结果;拿到第二个参数 module,修改其他模块的定义(虽然很有拓展性,但用在项目里是个搅屎棍)。

ES2015 Modules (2015): 就是我们现在的模块化方案,还没有被浏览器实现,大部分项目已通过 babel 或 typescript 提前体验。

围绕解决命名空间问题阶段

Nameflag 模式
JS模块化历史进程_第1张图片

  • 增加 Global 上的变量数目
  • 直接暴露,毫无封装性

Namespace 模式
JS模块化历史进程_第2张图片

  • 减少 Global 上的变量数目
  • 本质是对象,一点都不安全

IIFE 模式
JS模块化历史进程_第3张图片

  • 函数是 JavaScript 唯一的 Local Scope

Module 模式
JS模块化历史进程_第4张图片

  • 引入简单的依赖,把全局函数注入到匿名函数中
  • 现代模块实现的基石

模块模式深入——不同版本包之间关系的处理

——当考虑到加载顺序时,情况就变得复杂
拓展增强
JS模块化历史进程_第5张图片

  • 导入MODULE,扩充属性,导出MODULE
  • 需维护加载顺序

松散增强
JS模块化历史进程_第6张图片

  • 每个模块的松散约定
  • 异步加载,不再考虑加载顺序

——当考虑到不同版本融合时,情况就变得复杂
Sub-modules子模块
JS模块化历史进程_第7张图片

  • 更上一层的封装

Porxy模式
![上传中...]()

你可能感兴趣的:(javascript前端)