題目描述
簡單描述一下你所瞭解的JS模塊化
答案解析
這道題目主要考察對JS模塊化發展歷程的瞭解,以及發展中出現的一些規範和技術的掌握。
模塊化是每一種語言膨脹的畢竟之路,JS 也不例外,從原來的不支持模塊化,到現在支持模塊化,經歷了一個曲折的過程。
JS 模塊化產生的原因
以前JS本身沒有模塊化的概念和相關API,開發者一般都是在html中引入多個script標籤,業務邏輯複雜時,就會帶來很多問題,比如:
- 全局作用域下容易造成變量衝突
- 文件只能按照
script
的書寫順序進行加載 - 開發人員必須主觀解決模塊和代碼庫的依賴關係
- 在大型項目中各種資源難以管理,長期積累的問題導致代碼庫混亂不堪
模塊化的作用
它可以幫助開發者拆分和組織代碼,方便複用功能,降低項目開發的複雜度,提高可維護性。
模塊化方案及規範
一、閉包
在模塊化規範形成之前,JS 開發者通常利用閉包來解決全局作用域的污染問題,一般是通過自定義暴露行爲來區分私有成員和公有成員。
let myModule = (function (window) {
let name = '前端名獅' // private
// public
function setName(str) {
name = str
}
// public
function getName() {
return name
}
return { setName, getName } // 暴露行爲
})(window)
二、CommonJS
JS 一開始只能運行於瀏覽器中,所以一直被詬病。爲了突破JS的運行環境,CommonJS 規範出現了,它定義了一些規範和API(主要指非瀏覽器端的),使 JS 語言擁有了類似Python、Java等後端編程語言的能力。這樣的話,開發者可以使用CommonJS API編寫應用程序,然後這些應用可以運行在不同的JavaScript解釋器和不同的主機環境中。
2009年,美國程序員Ryan Dahl創造了node.js項目,將javascript語言用於服務器端編程。這標誌"Javascript模塊化編程"正式誕生。NodeJS是CommonJS規範的實現,webpack 也是以CommonJS的形式來書寫的。
node 模塊化示例:
// math.js 文件
let math = {
add: function(a, b) {
console.log(a + b);
}
}
module.exports = math;
// app.js 文件
let math = require('./math.js');
math.add(1, 1);
三、AMD && CMD
基於commonJS規範的nodeJS出來以後,服務端的模塊概念已經形成,如果將其應用到瀏覽器端,會出現什麼問題呢?
let math = require('./math.js');
math.add(1, 1);
上面代碼執行require的時候,需要等math.js文件加載完成,才能繼續執行,如果加載時間過長,頁面就會出現“假死”狀態。
這對服務器端不是一個問題,因爲所有的模塊都存放在本地硬盤,可以同步加載完成,等待時間就是硬盤的讀取時間。但是,對於瀏覽器,這卻是一個大問題,因爲模塊都放在服務器端,文件的加載需要通過HTTP請求,等待時間取決於網速的快慢,可能要等很長時間。
CommonJS是主要爲了JS在後端的表現制定的,是不適合前端的 ,所以出現了AMD、CMD、UMD等等一系列可以在瀏覽器等終端實現的異步加載的模塊化方案**,我們最熟悉的require.js就是AMD的產物,seajs是CMD的產物。**
// AMD
require(['a', 'b'], function(math) {
a.doSomething();
b.doSomething();
});
// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething();
...
var b = require('./b') // 依賴可以就近書寫
b.doSomething();
})
AMD 推崇依賴前置,CMD 推崇依賴就近。
上面這句話的意思就是:CMD加載完某個依賴模塊後並不執行,只是下載而已,在所有依賴模塊加載完成後進入主邏輯,遇到require語句的時候才執行對應的模塊,這樣模塊的執行順序和書寫順序是完全一致的。
四、ES6 Module
ES6的模塊化已經不是規範了,而是JS語言的特性。隨着ES6的推出,AMD和CMD也隨之成爲了歷史。ES6模塊與模塊化規範相比,有兩大特點:
- 模塊化規範輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。
- 模塊化規範是運行時加載,ES6 模塊是編譯時輸出接口。
模塊化規範輸出的是一個對象,該對象只有在腳本運行完纔會生成。而 ES6 模塊不是對象,ES6 module 是一個多對象輸出,多對象加載的模型。從原理上來說,模塊化規範是匿名函數自調用的封裝,而ES6 module則是用匿名函數自調用去調用輸出的成員。
注意:目前的瀏覽器幾乎都不支持 ES6 的模塊機制,所以我們要用 Babel 把 ES6 的模塊機制轉換成 CommonJS 的形式,然後使用 Browserify 或者 Webpack 這樣的打包工具把他們打包起來。達到模塊化加載效果類似於 seajs代碼
展望
1. Http 2.0 對 js 模塊化的推動
js 模塊化定義的再美好,瀏覽器端的支持粒度永遠是瓶頸,http 2.0 正是考慮到了這個因素,大力支持了 ES 2015 模塊化規範。
隨着 HTTP/2 流行起來,請求和響應可以並行,一次連接允許多個請求,對於前端來說,不再需要在開發和上線時再做編譯這個動作。
ES2015 Modules 也只是解決了開發的問題,由於瀏覽器的特殊性,還是要經過繁瑣打包的過程,等 Import,Export 和 HTTP 2.0 被主流瀏覽器支持,那時候纔是徹底的模塊化。
前端複雜度不斷提高,促使着模塊化的改進,代理(瀏覽器、node) 的支持程度,與前端特殊性(流量、緩存)可能前端永遠也離不開構建工具,新的標準會讓這些工作做的更好,同時取代、增強部分特徵,前端的未來是更加美好的,複雜度也更高。
2. html、css模塊化
文章中的 JS 的模塊化還不等於前端工程的模塊化,Web 界面是由 HTML、CSS 和 JS 三種語言實現,不論是 CommonJS 還是 AMD 包括之後的方案都無法解決 CSS 與 HTML 模塊化的問題。
html 與 css 模塊化問題正是以後的方向。一句話,模塊化仍在路上。
關注我
掃一掃 關注我的公衆號【前端名獅】,更多精彩內容陪伴你!