前端的模塊化之路經歷了漫長的過程,這裏根據大佬們寫的文章,將模塊化規範部分作了彙總和整理,想詳細瞭解的小夥伴能夠看浪裏行舟大神寫的前端模塊化詳解(完整版),但願讀完的小夥伴能有些收穫,也但願以爲有用的小夥伴能夠點個贊,筆芯。javascript
Node 應用由模塊組成,採用 CommonJS 模塊規範。每一個文件就是一個模塊,有本身的做用域。在一個文件裏面定義的變量、函數、類,都是私有的,對其餘文件不可見。在服務器端,模塊的加載是運行時同步加載的;在瀏覽器端,模塊須要提早編譯打包處理。前端
CommonJS規範加載模塊是同步的,也就是說,只有加載完成,才能執行後面的操做。java
基本語法:git
module.exports = value
或 exports.xxx = value
require(xxx)
,若是是第三方模塊,xxx爲模塊名;若是是自定義模塊,xxx爲模塊文件路徑可是,CommonJs有一個重大的侷限使得它不適用於瀏覽器環境,那就是require
操做是同步的。這對服務器端不是一個問題,由於全部的模塊都存放在本地硬盤,能夠同步加載完成,等待時間就是硬盤的讀取時間。可是,對於瀏覽器,這倒是一個大問題,由於模塊都放在服務器端,等待時間取決於網速的快慢,可能要等很長時間,瀏覽器處於」假死」狀態。es6
所以,瀏覽器端的模塊,不能採用」同步加載」(synchronous),只能採用」異步加載」(asynchronous),這就是AMD規範誕生的背景。github
特色:非同步加載模塊,容許指定回調函數,瀏覽器端通常採用AMD規範編程
表明做:require.js瀏覽器
用法:緩存
//定義沒有依賴的模塊
define(function(){
return 模塊
})
//定義有依賴的模塊
define(['module1', 'module2'], function(m1, m2){
return 模塊
})
//引入使用模塊
require(['module1', 'module2'], function(m1, m2){
//使用m1/m2
})
複製代碼
特色:專門用於瀏覽器端,模塊的加載是異步的,模塊使用時纔會加載執行服務器
表明做: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()
})
複製代碼
AMD和CMD最大的區別是對依賴模塊的執行時機處理不一樣,而不是加載的時機或者方式不一樣,兩者皆爲異步加載模塊。
AMD依賴前置,js能夠方便知道依賴模塊是誰,當即加載;
而CMD就近依賴,須要使用把模塊變爲字符串解析一遍才知道依賴了那些模塊,這也是不少人詬病CMD的一點,犧牲性能來帶來開發的便利性,實際上解析模塊用的時間短到能夠忽略。
一句話總結: 二者都是異步加載,只是執行時機不同。AMD是依賴前置,提早執行,CMD是依賴就近,延遲執行。
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 模塊的設計思想是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。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)
複製代碼
① 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 模塊還有如下好處:
navigator
對象的屬性。Math
對象),將來這些功能能夠經過模塊提供。Node.js
中運行。不過,依賴SPM打包,模塊的加載邏輯偏重。以上是本篇文章的內容,歡迎你們提出本身的想法,咱們一塊兒學習進步,與君共勉。