全文主要整理自摘自《Webpack中文指南》(好文,建议直接去看,以下仅对该系列文章中的《历史发展》篇幅进行备份——也整理了点其他内容)

模块化

模块化是老生常谈了,这里不做阐述。

模块化管理需要具备:

1. 定义封装的模块。

2. 定义新模块对其他模块的依赖。

3. 可对其他模块的引入支持。

要通用,则必须要有规范化,因此一系列的标准应运而生。

现状

伴随着移动互联的大潮,当今越来越多的网站已经从网页模式进化到了 Webapp 模式。它们运行在现代的高级浏览器里,使用 HTML5、 CSS3、 ES6 等更新的技术来开发丰富的功能,网页已经不仅仅是完成浏览的基本需求,并且webapp通常是一个单页面应用,每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的 JavaScript 代码,这给前端开发的流程和资源组织带来了巨大的挑战。

前端开发和其他开发工作的主要区别,首先是前端是基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。

模块系统的演进

模块系统主要解决模块的定义、依赖和导出,先来看看已经存在的模块系统。

<script>标签

<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>
<script src="module3.js"></script>

这是最原始的 JavaScript 文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在 window 对象中,不同模块的接口调用都是一个作用域中,一些复杂的框架,会使用命名空间的概念来组织这些模块的接口,典型的例子如 YUI 库。

这种原始的加载方式暴露了一些显而易见的弊端:

  • 全局作用域下容易造成变量冲突
  • 文件只能按照 <script> 的书写顺序进行加载
  • 开发人员必须主观解决模块和代码库的依赖关系
  • 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪

CommonJS

历史

  2009年1月,Mozilla 的工程师 Kevin Dangoor 创建了这个项目,当时的名字是 ServerJS。其是以在浏览器环境之外构建 JavaScript 生态系统为目标而产生的项目,比如在服务器和桌面环境中。

  2009年8月,这个项目改名为 CommonJS,以显示其 API 的更广泛实用性。

  2013年5月,Node.js 的包管理器 NPM 的作者 Isaac Z. Schlueter 说 CommonJS 已经过时,Node.js 的内核开发者已经废弃了该规范

定义

  服务器端的 Node.js 遵循 CommonJS规范,该规范的核心思想是允许模块通过 require 方法来同步加载所要依赖的其他模块,然后通过 exports 或 module.exports 来导出需要暴露的接口。

  require("module");
  require("../file.js");
  exports.doStuff = function() {};
  module.exports = someValue;

优点:

  • 服务器端模块便于重用
  • NPM 中已经有将近20万个可以使用模块包
  • 简单并容易使用

缺点:

  • 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
  • 不能非阻塞的并行加载多个模块

实现:

  • 服务器端的 Node.js
  • Browserify,浏览器端的 CommonJS 实现,可以使用 NPM 的模块,但是编译打包后的文件体积可能很大
  • modules-webmake,类似Browserify,还不如 Browserify 灵活
  • wreq,Browserify 的前身

AMD

AMD(异步模块定义)是为浏览器环境设计的,因为 CommonJS 模块系统是同步加载的,当前浏览器环境还没有准备好同步加载模块的条件。

AMD 定义了一套 JavaScript 模块依赖异步加载标准,来解决同步加载的问题。

Asynchronous Module Definition 规范其实只有一个主要接口 define(id?, dependencies?, factory),它要在声明模块的时候指定所有的依赖 dependencies,并且还要当做形参传到 factory 中,对于依赖的模块提前执行,依赖前置。

define("module", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });

优点:

  • 适合在浏览器环境中异步加载模块
  • 可以并行加载多个模块

缺点:

  • 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不顺畅
  • 不符合通用的模块化思维方式,是一种妥协的实现

实现:

CMD

Common Module Definition 规范和 AMD 很相似,尽量保持简单,并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性。

define(function(require, exports, module) {
var $ = require('jquery');
var Spinning = require('./spinning');
exports.doSomething = ...
module.exports = ...
})

优点:

  • 依赖就近,延迟执行
  • 可以很容易在 Node.js 中运行

缺点:

  • 依赖 SPM 打包,模块的加载逻辑偏重

实现:

UMD

Universal Module Definition 规范类似于兼容 CommonJS 和 AMD 的语法糖,是模块定义的跨平台解决方案。

ES6 模块

EcmaScript6 标准增加了 JavaScript 语言层面的模块体系定义。ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。

import "jquery";
export function doStuff() {}
module "localModule" {}

优点:

  • 容易进行静态分析
  • 面向未来的 EcmaScript 标准

缺点:

  • 原生浏览器端还没有实现该标准
  • 全新的命令字,新版的 Node.js才支持

实现:

期望的模块系统

可以兼容多种模块风格,尽量可以利用已有的代码,不仅仅只是 JavaScript 模块化,还有 CSS、图片、字体等资源也需要模块化。

前端模块加载

前端模块要在客户端中执行,所以他们需要增量加载到浏览器中。

模块的加载和传输,我们首先能想到两种极端的方式,一种是每个模块文件都单独请求,另一种是把所有模块打包成一个文件然后只请求一次。显而易见,每个模块都发起单独的请求造成了请求次数过多,导致应用启动速度慢;一次请求加载所有模块导致流量浪费、初始化过程慢。这两种方式都不是好的解决方案,它们过于简单粗暴。

分块传输,按需进行懒加载,在实际用到某些模块的时候再增量更新,才是较为合理的模块加载方案。

要实现模块的按需加载,就需要一个对整个代码库中的模块进行静态分析、编译打包的过程。

所有资源都是模块

在上面的分析过程中,我们提到的模块仅仅是指JavaScript模块文件。然而,在前端开发过程中还涉及到样式、图片、字体、HTML 模板等等众多的资源。这些资源还会以各种方言的形式存在,比如 coffeescript、 less、 sass、众多的模板库、多语言系统(i18n)等等。

如果他们都可以视作模块,并且都可以通过require的方式来加载,将带来优雅的开发体验,比如:

require("./style.css");
require("./style.less");
require("./template.jade");
require("./image.png");

那么如何做到让 require 能加载各种资源呢?

静态分析

在编译的时候,要对整个代码进行静态分析,分析出各个模块的类型和它们依赖关系,然后将不同类型的模块提交给适配的加载器来处理。比如一个用 LESS 写的样式模块,可以先用 LESS 加载器将它转成一个CSS 模块,在通过 CSS 模块把他插入到页面的 <style> 标签中执行。Webpack 就是在这样的需求中应运而生。

同时,为了能利用已经存在的各种框架、库和已经写好的文件,我们还需要一个模块加载的兼容策略,来避免重写所有的模块。

那么接下来,让我们开始 Webpack 的神奇之旅吧。

模块化(CommonJs、AMD、CMD、UMD)发展历史与优缺点的更多相关文章

  1. JavaScript模块化CommonJS/AMD/CMD/UMD/ES6Module的区别

    目录 JS-模块化进程 原始的开发方式 CommonJS && node.js AMD && Require.js CMD && Sea.js UMD ...

  2. Javascript模块化编程之CommonJS,AMD,CMD,UMD模块加载规范详解

    JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发?     模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问 题进行系 ...

  3. JavaScript模块化演变 CommonJs,AMD, CMD, UMD(一)

    原文链接:https://www.jianshu.com/p/33d53cce8237 原文系列2链接:https://www.jianshu.com/p/ad427d8879cb 前端完全手册: h ...

  4. 关于 CommonJS AMD CMD UMD 规范的差异总结

    一.CommonJS 主要是用于服务器端的规范,比如目前的nodeJS. 根据CommonJS规范,一个单独的文件就是一个模块.每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函 ...

  5. 关于 CommonJS AMD CMD UMD 规范的差异总结(转)

    根据CommonJS规范,一个单独的文件就是一个模块.每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的. // foo.js var ...

  6. 关于 CommonJS AMD CMD UMD

    1. CommonJS CommonJS 原来叫 ServerJS, 是服务器端模块的规范,Node.js采用了这个规范. 根据CommonJS规范,一个单独的文件就是一个模块.加载模块使用requi ...

  7. CommonJS/AMD/CMD/UMD

    为什么会有这几种模式? 起源:Javascript模块化 模块化就是把复杂问题分解成不同模块,这样可维护性高,从而达到高复用,低耦合. 1.Commonjs CommonJS是服务器端模块的规范,No ...

  8. CommonJS/AMD/CMD/UMD概念初探

    1.CommonJS是一种规范,NodeJS是这种规范的实现. 1.1.CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作. 参考: http://www.commonjs.org ...

  9. CommonJS, AMD, CMD是什么及区别--简单易懂有实例

    CommonJS, AMD, CMD都是JS模块化的规范. CommonJS是服务器端js模块化的规范,NodeJS是这种规范的实现. AMD(异步模块定义)和CMD(通用模块定义)都是浏览器端js模 ...

  10. Commonjs,AMD,CMD和UMD的差异

    CommonJS 一种服务器端模块化的规范,Nodejs实现了这种规范,所以就说Nodejs支持CommonJS. CommonJS分为三部分: require 模块加载 exports 模块导出 m ...

随机推荐

  1. UITextView被键盘遮挡的处理

    这个应该是一个通用的任务了吧,键盘弹出来的时候,UITextView(或者UITextField)会被遮挡. 解决的办法就不是很能通用了. 1. 如果有UIScrollView做父view的话只需要滚 ...

  2. C#中的隐藏方法

    在C#中要重写基类的方法,C#提倡在基类中使用virtual来标记要被重写的方法,在子类也就是派生类中用voerride关键字来修饰重写的方法. 如果要是项目中前期考虑不足,我没有在基类(ClassA ...

  3. Delphi中array of const应用

    Delphi的Format函数大家都用得很多,第二个参数用着确实很方便.最近在数据库开发应用中需要自己创建一个带array of const参数的函数,对于常用的类型String,Integer,Po ...

  4. PHP设置会话(Session)超时过期时间实现登录时间限制

    最近某个PHP项目用到了限制登录时间的功能,比如用户登录系统60分钟后如果没有操作就自动退出,我搜索了网络收集了有以下方法可供参考. 第一种方法即设置php.ini配置文件,设置session.gc_ ...

  5. NoHostAvailableException: All host(s) tried for query failed (no host was tried)

    NoHostAvailableException: All host(s) tried for query failed (no host was tried) cassandra连接报错:tried ...

  6. php正則表達式中的非贪婪模式匹配的使用

    php正則表達式中的非贪婪模式匹配的使用 通常我们会这么写: $str = "http://www.baidu/.com? url=www.sina.com/"; preg_mat ...

  7. 【数据挖掘】分类之decision tree(转载)

    [数据挖掘]分类之decision tree. 1. ID3 算法 ID3 算法是一种典型的决策树(decision tree)算法,C4.5, CART都是在其基础上发展而来.决策树的叶子节点表示类 ...

  8. Duang,HUAWEI DevEco IDE全面升级啦

    想感受全新UI带来的视觉及交互体验. HiKey970开发板调测. HiAI API推荐和收藏. 深度AI模型分析等新功能, 体验高清晰度和流畅度的远程AI真机调测吗? 全新的UI设计 采用最优秀的视 ...

  9. Android中关于JNI 的学习(四)简单的样例,温故而知新

    在第零篇文章简单地介绍了JNI编程的模式之后.后面两三篇文章,我们又针对JNI中的一些概念做了一些简单的介绍,也不知道我究竟说的清楚没有.但相信非常多童鞋跟我一样.在刚開始学习一个东西的时候,入门最好 ...

  10. 第一篇: Ansible 介绍

    应用场景:   BOSS:运维帮忙把所有的服务器tomcat 重启一下,谢谢!(tomcat 服务有2K台) 运维:………… 运维:  啪啪啪啪啪啪啪啪..........(键盘的声音响彻办公室) B ...