前语

前端的模块化之路阅历了绵长的进程,想具体了解的小伙伴能够看浪里行舟大神写的前端模块化详解(完整版),这儿依据几位大佬们写的文章,将模块化标准部分做了汇总和收拾,期望读完的小伙伴能有些收成,也期望觉得有用的小伙伴能够点个赞,笔芯。

什么是模块

  • 将一个杂乱的程序依据必定的规矩(标准)封装成几个块(文件), 并进行组合在一同
  • 块的内部数据与完成是私有的,仅仅向外部露出一些接口(办法)与外部其它模块通讯

CommonJS

Node 运用由模块组成,选用 CommonJS 模块标准。每个文件便是一个模块,有自己的效果域。在一个文件里边界说的变量、函数、类,都是私有的,对其他文件不行见。在服务器端,模块的加载是运转时同步加载的;在浏览器端,模块需求提早编译打包处理。

CommonJS标准加载模块是同步的,也便是说,只要加载完结,才干履行后边的操作。

根本语法:

  • 露出模块:module.exports = value 或 exports.xxx = value
  • 引进模块:require(xxx),如果是第三方模块,xxx为模块名;如果是自界说模块,xxx为模块文件途径

可是,CommonJs有一个严重的限制使得它不适用于浏览器环境,那便是require操作是同步的。这对服务器端不是一个问题,由于一切的模块都存放在本地硬盘,能够同步加载完结,等待时刻便是硬盘的读取时刻。可是,关于浏览器,这却是一个大问题,由于模块都放在服务器端,等待时刻取决于网速的快慢,或许要等很长时刻,浏览器处于”假死”状况。

因而,浏览器端的模块,不能选用”同步加载”(synchronous),只能选用”异步加载”(asynchronous),这便是AMD标准诞生的布景。

AMD

特色:非同步加载模块,答应指定回调函数,浏览器端一般选用AMD标准

代表作:require.js

用法:

//界说没有依靠的模块
define(function(){
return 模块
})
//界说有依靠的模块
define(['module1', 'module2'], function(m1, m2){
return 模块
})
//引进运用模块
require(['module1', 'module2'], function(m1, m2){
//运用m1/m2
})

CMD

特色:专门用于浏览器端,模块的加载是异步的,模块运用时才会加载履行

代表作:Sea.js

用法:

//界说没有依靠的模块
define(function(require, exports, module){
exports.xxx = value
module.exports = value
})
//界说有依靠的模块
define(function(require, exports, module){
//引进依靠模块(同步)
var module2 = require('./module2')
//引进依靠模块(异步)
require.async('./module3', function (m3) {
})
//露出模块
exports.xxx = value
})
//引进运用模块
define(function (require) {
var m1 = require('./module1')
var m4 = require('./module4')
m1.show()
m4.show()
})

CMD与AMD差异

AMD和CMD最大的差异是对依靠模块的履行机遇处理不同,而不是加载的机遇或许办法不同,二者皆为异步加载模块。

AMD依靠前置,js能够便利知道依靠模块是谁,当即加载;

而CMD就近依靠,需求运用把模块变为字符串解析一遍才知道依靠了那些模块,这也是很多人诟病CMD的一点,献身性能来带来开发的便利性,实际上解析模块用的时刻短到能够疏忽。

一句话总结:

两者都是异步加载,仅仅履行机遇不一样。AMD是依靠前置,提早履行,CMD是依靠就近,推迟履行。

UMD

UMD是AMD和CommonJS的糅合:

AMD模块以浏览器榜首的准则开展,异步加载模块。

CommonJS模块以服务器榜首准则开展,挑选同步加载,它的模块无需包装(unwrapped modules)。

这迫使人们又想出另一个更通用的方法UMD (Universal Module Definition)。期望处理跨渠道的处理方案。

UMD先判别是否支撑Node.js的模块(exports)是否存在,存在则运用Node.js模块方法。

在判别是否支撑AMD(define是否存在),存在则运用AMD办法加载模块。

(function (window, factory) {
if (typeof exports === 'object') {

module.exports = factory();
} else if (typeof define === 'function' && define.amd) {

define(factory);
} else {

window.eventUtil = factory();
}
})(this, function () {
//module ...
});

ES6模块化

ES6 模块的规划思维是尽量的静态化,使得编译时就能确认模块的依靠联系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运转时确认这些东西。比方,CommonJS 模块便是目标,输入时有必要查找目标特点。

ES6 Module默许现在还没有被浏览器支撑,需求运用babel,在日常写demo的时分经常会显现这个过错:

ES6模块运用import关键字导入模块,export关键字导出模块:

/** 导出模块的办法 **/
var a = 0;
export { a }; //榜首种

export const b = 1; //第二种

let c = 2;
export default { c }//第三种
let d = 2;
export default { d as e }//第四种,别号
/** 导入模块的办法 **/
import { a } from './a.js' //针对export导出办法,.js后缀可省掉
import main from './c' //针对export default导出办法,运用时用 main.c
import 'lodash' //仅仅履行lodash模块,可是不输入任何值

命名式导出与默许导出

export {<变量>}这种办法一般称为 命名式导出 或许 签字导出,导出的是一个变量的引证

export default这种办法称为 默许导出 或许 匿名导出,导出的是一个

举例:

// a.js
let x = 10
let y = 20
setTimeout(()=>{
x = 100
y = 200
},100)
export { x }
export default y
// b.js
import { x } from './a.js'
import y from './a.js'
setTimeout(()=>{
console.log(x,y) // 100,20
},100)

ES6 模块与 CommonJS 模块的差异

① CommonJS 模块输出的是一个值的复制,ES6 模块输出的是值的引证。

CommonJS 模块输出的是值的复制,也便是说,一旦输出一个值,模块内部的改变就影响不到这个值。并且,CommonJS 模块不管加载多少次,都只会在榜首次加载时运转一次,今后再加载,回来的都是榜首次运转成果的缓存,除非手动铲除体系缓存。

ES6 模块的运转机制与 CommonJS 不一样,JS 引擎对脚本静态剖析的时分,遇到模块加载指令import,就会生成一个只读引证,比及脚本真实履行时,再依据这个只读引证,到被加载的那个模块里边去取值。换句话说,ES6 的import有点像 Unix 体系的“符号衔接”,原始值变了,import加载的值也会跟着变。因而,ES6 模块是动态引证,并且不会缓存值,模块里边的变量绑定其地点的模块。

② CommonJS 模块是运转时加载,ES6 模块是编译时输出接口。

CommonJS 加载的是一个目标(即module.exports特点),该目标只要在脚本运转完才会生成。即在输入时是先加载整个模块,生成一个目标,然后再从这个目标上面读取办法,这种加载称为“运转时加载”。

例如:

// CommonJS模块
let { stat, exists, readFile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

上面代码的本质是全体加载fs模块(即加载fs的一切办法),生成一个目标(_fs),然后再从这个目标上面读取 3 个办法。由于只要运转时才干得到这个目标,导致彻底没办法在编译时做“静态优化”。

ES6 模块不是目标,它的对外接口仅仅一种静态界说,在代码静态解析阶段就会生成。经过export指令显式指定输出的代码,import时选用静态指令的方法。即在import时能够指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”或许“静态加载”。

// ES6模块
import { stat, exists, readFile } from 'fs';

上面代码的本质是从fs模块加载 3 个办法,其他办法不加载。即 ES6 能够在编译时就完结模块加载,功率要比 CommonJS 模块的加载办法高。当然,这也导致了无法引证 ES6 模块自身,由于它不是目标

由于 ES6 模块是编译时加载,使得静态剖析成为或许。有了它,就能进一步拓展 JavaScript 的语法,比方引进宏(macro)和类型查验(type system)这些只能靠静态剖析完成的功用。

除了静态加载带来的各种优点,ES6 模块还有以下优点:

  • 不再需求UMD模块格局了,将来服务器和浏览器都会支撑 ES6 模块格局。现在,经过各种东西库,其完成已做到了这一点。
  • 将来浏览器的新API 就能用模块格局供给,不再有必要做成全局变量或许navigator目标的特点。
  • 不再需求目标作为命名空间(比方Math目标),未来这些功用能够经过模块供给。

总结

  1. CommonJS标准首要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,由于同步意味着堵塞加载,浏览器资源是异步加载的,因而有了AMD、CMD处理方案。
  2. AMD标准在浏览器环境中异步加载模块,并且能够并行加载多个模块。不过,AMD标准开发本钱高,代码的阅览和书写比较困难,模块界说办法的语义不顺利。
  3. CMD标准与AMD标准很类似,都用于浏览器编程,依靠就近,推迟履行,能够很简略在Node.js中运转。不过,依靠SPM打包,模块的加载逻辑侧重。
  4. ES6 在言语标准的层面上,完成了模块功用,并且完成得适当简略,彻底能够替代 CommonJS 和 AMD 标准,成为浏览器和服务器通用的模块处理方案。

以上是本篇文章的内容,欢迎咱们提出自己的主意,咱们一同学习前进,与君共勉。

推荐阅读