JavaScript模块化演化史

前言

本文可能不涉及一些基础概念,好比什么是模块,对每种方案也没有都作详细的解释(若是解释的话,每一个都值得一篇文章),准备和落实这篇文章主要是有两方面缘由。javascript

  1. 平时遇到的一些名词有些摸不着头脑,老是大概知道是个什么意思,但细想又不知道其原理和联系,好比平时遇到CommonJS,seaJS,CMD,AMD,UMD,Browserify,RequireJS,知道他们和js模块有关,但具体是什么关系,js模块化应该用哪一个的倒是不知道。
  2. 使用webpack作模块打包,不知道过程当中作了什么(除了plugins和loader),是从什么样的模块打包成什么样的模块,最后又变成了什么。

综上两点,就是对JS的模块不熟悉致使的。前端

本文目标

对JS模块化有一个直面的了解,并能捋清楚各个规范之间的之间的联系,再遇到这些名词不会再有它认识我,我不认识它的感受java

演变过程

首先来讲为何须要模块化,这个你们内心应该都有一两个答案,好比避免命名冲突啊,全局变量过多啊,依赖很差管理啊等等等等。总得来讲模块化大方向上有几个好处。node

  1. 易于维护
  2. 命名空间(解决命名冲突)
  3. 复用性
  4. ...

整体上模块化基本上是一个百利无一害的实现jquery

原始时代

开始JS没有模块的概念,所以致使了不少问题,好比命名冲突,好比:webpack

var origin = 100;
....其余代码
var origin = 1232;
....其余代码
console.log(origin)//目标是让它输出100,但实际会输出1232,由于后来的命名覆盖
复制代码

还有依赖问题,好比a.js依赖了b.js,那么下载时必须是先下载b,在下载a,顺序必定不能错。web

IIFE

为了解决这些没有模块带来的问题,前辈们提出了使用IIFE来模仿模块,以下:gulp

(function(){
	var name = 'wingtao;
	var sayHello = function (){
		console.log('hello '+name);
	}
	sayHello(); // hello wingtao
})()
复制代码

上面利用当即执行函数模拟了一个模块,该模块中的变量外界没法访问,避免了命名冲突的问题,jQuery就用了这种方式,api

(function(global){
	global.jquery = ...
})(window);
复制代码

但仍是没有彻底实现模块化,如对模块依赖的管理、如何将api暴露出来而不污染全局环境浏览器

CommonJS && nodejs

CommonJS社区首先提出了模块化的规范CommonJS,因此CommonJS是一个规范!在node上只须要简单的require和exports就能够实现模块的导入和导出,以下:

a.js
exports.add = function(a,b){
	return a+b;
}

b.js 
var add = require('a.js').add;
console.log(add(1,2))//3
复制代码

看起来很是棒!并且nodeJS模块实现了这种规范,意味着在node中能够直接使用这种方式。

CommonJS && Browserify

既然服务端能实现了这种模块化的规范,浏览器上对此也是很是迫切的,天然也是想要实现这块,可是直接拿来用是有一些问题,

  1. node中require是同步加载的,由于直接从内存或硬盘里读就能够了,而在浏览器上不能同步加载,由于浏览器上每个文件都是须要下载下来的,都是须要时间的,并且浏览器上下载js都是经过script来加载的,不能同步执行,因此也就没办法同步加载模块了。
  2. 没有当即执行函数的包裹,加载的模块变量又暴露在全局上了。

所以若是想在浏览器上使用CommonJS是须要改造的,对此人们分红了几派,一派是认为仍是按照CommonJS规范来,只是加上函数包裹和异步加载,在浏览器上能执行就好了;一派认为CommonJS不适合浏览器端,须要一个新的规范;第三方是个“和稀泥”的,认为CommonJS和从新改革都有可取之处,因此各取所长。

其中第一派坚持使用CommonJS的作出了浏览器端的实现Browserify,名字也很形象,Browserify能够将node端模块文件转换为浏览器可识别的模块文件。因此Browserify是CommonJS在浏览器端的实现

AMD && RequireJS

AMD(规范)其实就是上面说的第二派,就是抛弃CommonJS,提出新的可异步加载的模块规范。AMD最大的特色即是能够异步加载模块,它的实现是RequireJS,编写时像这样:

define(['myModule', 'myOtherModule'],function(myModule, myOtherModule) {
	console.log(myModule.hello());
});
复制代码

过程是先加载依赖myModule,myOtherModule(后台不阻塞的方式加载),加载完成后执行回调函数,其中回调函数的参数即是已经加载完成的模块。其实AMD仍是有不少问题的,好比define的时候全部依赖要挨个写一遍,好比无论如今用不用的到都会把依赖先下载下来,不过这些问题AMD都有优化,这里不提。

UMD

UMD全称是Universal Module Definition,目的兼容CommonJS和AMD,因此它会作一层判断,判断当前环境是浏览器仍是node,若是是浏览器则使用AMD,node环境使用CommonJS方式,UMD实现了两种环境的兼容,但同时也致使了十分臃肿,肉眼观察实在有点费劲。

CMD && seaJS

提起CMD,不怕被笑话,我以前还觉得和CommonJS是同一个东东呢😂。seaJS是阿里前端工程师玉伯作出来的,并提出了CMD,CMD吸收了AMD和CommonJS二者的优势,融合了百家之长(但好像只在国内有影响,国外影响有限),因此CMD是规范而seajs是它的实现。

ES6 Module

以上说的种种方式都是由于ECMA缺少官方的模块规范才出来的,既然对模块化的需求这么旺盛,官方在ES2015(ES6)里也就提出了官方的模块化方案,主要使用import和export,用法很是简单,并且它和以前的方案的区别除了它是官方提出而且写法简单以外,还有重要的一点就是它是静态解析的,什么是静态解析呢?另开一篇文章再讲,不过这个特性能够带来不少优化,如tree-shaking。ES6模块机制理论上是浏览器原生支持的,但实际上如今支持度还不够,这个你们应该也能理解,不过在将来应该可能就能在浏览器中直接加载导出模块了,

webpack && gulp && rollup

这些工具其实和上面讲的模块规范已经不是一个维度的事情了,上面说的是模块化,这些是一些能够打包的工具,打包什么呢?模块!这就是他们之间的联系了。就不展开讲了,这是三个东西太多了。

总结

简单总结一下上面讲的东西,其实就是讲了下js模块的几个方案,每一个方案都有本身的规范,然而只有规范还不行,须要有实现来支持它,因此总得来讲就是:

环境 规范 实现
node CommonJS nodejs 模块
浏览器 CommonJS Browserify
浏览器 AMD RequireJS
浏览器 UMD 👆二者
浏览器 CMD SeaJS

写下来这些就是但愿本身能对js模块化有一个较为全面的了解,面对这些名词再也不默认,并了解他们之间的关系。

文章仓促,有不足之处但愿多多指出。

若是你看完对你有些许的帮助,那是意外之喜😊。

参考文章

JavaScript模块:指南

JavaScript模块:模块打包

相关文章
相关标签/搜索