Javascript模組全攬

RecoReco發表於2018-09-03

之前寫的文章急速Js全棧教程得到了不錯的閱讀量,霸屏掘金頭條3天,點贊過千,閱讀近萬,甚至還有人在評論區打廣告,可見也是一個小小的生態了;)。看來和JS全棧有關的內容,還是有人頗有興趣的。

Javascript模組全攬

這次文章的內容,是JavaScript模組。JavaScript Module 真是很討厭,但是不得不瞭解的話題。奇葩在於:

  1. 它一個非常老的語言,並且使用非常廣泛
  2. 可是它很多年來也不支援模組。這得廠家當前是多大的心呢
  3. 再一個可是,它可以直接用現有的語言機制,實現自己的模組,這個就厲害了,因為它釋放了社群的力量。事實證明,社群果然不可小看,這個年代,螞蟻雄兵勝過大象的
  4. 再再一個但是,它的模組還可以有很多型的,這說的是分裂
  5. 這麼多型的模組,還搞了各自獨立的標準出來,這說的是整合

最近的ES2017,終於在前端也有了媲美后端的模組,但是大家並不準備把它用起來,很多人表示需要繼續Webpack玩轉ES6模組

把ES6模組真用的起來,可以不在乎Webpack等打包工具帶來的載入優化,各種小檔案不必打包這點來說,我看還得加上HTTP/2的配合就好很多了。這也是文章將要介紹的一個主旨吧。ES6模組的引入,確實有可能對當前主流的打包模式有些影響,參考文章6內有所論述

文章自然也不少,但是寫作此文的理由還是存在:

  1. 我還沒有看到一個完整的全覽,並且結合HTTP/2的更加沒有看到。
  2. 而且,在我看來,即使有了ES6模組,也得了解和學習之前拼出來的各種模組,因為社群內的程式碼還大量的使用這樣的模組,其中的一些設計模式,比如IIFE,也是值得一看的。
  3. 看到JS社群的熱情和推動力,相信JS發展的未來是美好的

目錄

  1. 最古老的模組載入<script>標籤
  2. 此方法的若干問題
  • 全域性變數。全域性命名汙染和命名衝突
  • 依賴管理。都需要HTML管理,而不是分層管理依賴,多檔案載入次序非常關鍵
  • 效率。太多HTTP請求,和並行載入效率低下
  1. 有問題引發的解決方法
  • 命令空間,匿名閉包、依賴引入
  1. 當前主流的模組技術
  • Nodejs的做法,Commonjs方案
  • Nodejs借鑑
  • Require.js實踐,AMD和CMD,依賴就近原則
  • 從手寫模組,到自動編譯,Browerify,Webpack,Rollup
  1. 剛剛落地的模組技術
  • ES6模組,官方發力,對現有技術的影響
  • 彌補ES6問題,HTTP/2
  1. 最佳實踐

從指令碼載入開始

一切從Javascript的載入開始,自有Javascript依賴,第一個載入模組的方案就是使用HTML標籤,也就是<script>標籤。也就是說,Javascript本身根本就沒有模組和載入的定義,它是利用HTML來完成本該自己做的工作。

這是初學者遇到的第一個令人困惑的問題。這樣的語言,根本就是一個玩具!也許有些尷尬,但是現實就是如此。並且如此的載入方案,在稍微大點的工程中,會遇到幾個違反常識的問題:

  1. 全域性命名汙染。就是說每一個被載入的模組都會引入新的全域性變數,他們會汙染全域性空間,而且必須小心命名,避免名字的衝突
  2. 依賴關係單一。此種載入方式,必須按照依賴關係排序,依賴性最大的模組一定要放到最後載入,當依賴關係很複雜的時候,程式碼的編寫和維護都會變得困難。
  3. 載入和執行效率難以細顆粒度的調優。一個個的按依賴次序載入和執行。雖然載入往往是可以並行的,但是執行時序列的。載入的時候,瀏覽器會停止網頁渲染,載入檔案越多,網頁失去響應的時間就會越長

還是從一個案例開始。這個案例,不會做任何有意義的工作,也不會做什麼功能演示,而只是驗證古典的Javascript載入的能力和限定,驗證這些問題的存在,進而找到解決問題的方法。

假設我們現在有一個主程式,它在index.html內,一個模組dep1,一個模組dep2,依賴關係是index.html依賴dep1,dep1依賴dep2。程式碼都不復雜,就是直接列表如下:

檔案index.html

// index.html
<script src="dep2.js"></script>
<script src="dep1.js"></script>
<script type="text/javascript">
	console.log(dep1())
</script>
複製程式碼

檔案dep1.js

var v1 = "dep1"
function dep1(){
	return v1+"-"+dep2()
}
複製程式碼

檔案dep2.js

var v2 = "dep2"
function dep2(){
	return v2
}
複製程式碼

當使用瀏覽器載入index.html檔案時,如我所願,它會隨後載入dep2,dep1,並呼叫函式dep1,後者呼叫dep2,然後在控制檯輸出:

dep1+dep2
複製程式碼

功能是有效的,依賴關係是是對的,輸出也是如期望的。但是它也帶來了額外的問題:

  1. 全域性變數汙染。在本案例中,可以在console內驗證,發現變數v1,v2,函式dep1,dep2都是全域性變數。但是由於script的載入機制,以及當前採用的Javascript函式和變數的定義不是區域性化的,導致了這樣的問題。
  2. 依賴關係並不嚴密。事實上,dep2內的引入變數和函式,只有dep1看得到即可,無需匯入到全域性變數內。
  3. 載入和執行效率難以細顆粒度的調優。本例內,dep1依賴dep2,它們被並行轉入,但是執行必須是序列的,首先執行dep2,然後執行dep1,在此案例中,這樣做是合適的,但是有不少程式碼模組之間並不存在依賴關係,它們完全可能併發裝入併發執行,但是使用script裝入是不能如此的,它會按照標籤的次序一個個的執行。如果有比較好的指定依賴關係的方法就好了。

討論到此,我感覺我在重複先輩們的話,實際上1960年代,第一屆軟體工程會議,就提出了模組化程式設計的概念,並且在之後多年一直努力的批評全域性變數和Goto語句了。有時候,你會發現,這樣看起來非常不濟的語言,卻可以在現實的專案中如魚得水,發展的非常的好。而軟體工程思想指導下的一些名流語言卻早早夭折。這是另外一個有趣的話題了,或許以後有機會談到。

後端的借鑑

後端Nodejs乾淨利索的解決了此問題。做法就是對每一個裝入的模組都注入一個require函式和一個exports物件。其中require函式可以被模組用來引入其他模組,而exports物件則被用來引出當前模組的功能介面。還是以前文提到的作為案例,做法就是:

檔案index.js

// index.js
var d = require('./dep1')
console.log(d.dep1())
複製程式碼

檔案dep1.js

var d = require('./dep2')
var v1 = "dep1"
function dep1(){
	return v1+"-"+d.dep2()
}
exports.dep1 = dep1
複製程式碼

檔案dep2.js

var v2 = "dep2"
function dep2(){
	return v2
}
exports.dep2 = dep2
複製程式碼

執行命令:

$ node index.js 
dep1-dep2
複製程式碼

這裡有一點變化,就是在nodejs內使用index.js代替了index.html。可以看到:

  1. Nodejs提供了很好的區域性化定義變數和函式的能力,如果使用exports宣告引出,其他模組看不到本模組的定義。比如v2變數沒有宣告引出,當然實際上在本案例內本來也不必引出,那麼在dep1內並不會看到v2變數。類似的v1也不會出現在index.js內。
  2. Nodejs提供了更加細粒度的依賴關係。index.js依賴dep1,但是並不依賴於dep2,那麼index.js就只要引入dep1,而不必同時引入dep2。這樣的依賴關係,更加符合實際工程程式碼的需求,而不是一股腦的、不分層次的引入全部需要用到的程式碼。

在傳統的伺服器開發的諸多語言中,模組都是最基礎也是最必備的,像是JavaScript連個內建模組支援都沒有的是不常見的(或者說根本沒有?)。使用諸如的require和exports,就在後端乾淨利索的解決了困惱前端的模組問題,這不免讓前端覺得應該效仿之。當然,Nodejs載入模組是同步的,這個是不能在前端效仿的。因為後端從磁碟載入程式碼,速度根本不是問題,而前端載入的都是從網路上進行的, 如果同步的話,加上Javas本身的單執行緒限定,整個UI就會因為載入程式碼而被卡死的。對比下兩者的速度差異,你就明白了:

硬碟 I/O		
-----------------
HDD:	100 MB/s	
SSD:	600 MB/s	
SATA-III:	6000 Mb/s	
-----------------
網速 I/O
ADSL:	4 Mb/s
4G:	100 Mb/s
Fiber:	100 Mb/s
複製程式碼

借鑑後的樣子,先看看Modules/Async規範

思路倒也簡單,只要自己編寫一個庫,有它來非同步載入其他模組,並在載入時注入需要的require和exports即可。這方面的庫有幾個,比如requirejs,sea.js等。因為我們只是為了講清楚概念和思路,因此會那概念上最清晰,和Nodejs最為一致的庫來說明問題,並不會因為那個更加主流而去選擇它。從這個標準看,sea.js是說明概念問題的最佳模組裝入庫。

sea.js 是一個模組載入器,模組載入器需要實現兩個基本功能:

  1. 實現模組定義規範
  2. 載入執行符合規範的模組

核心落腳點,就在規範二字上。sea.js要求模組編寫必須在真正的程式碼之外套上一層規定的程式碼包裝,樣子看起來是這樣的:

define(function(require, exports, module) {
    // 模組程式碼
});
複製程式碼

通過傳遞一個簽名為function(require, exports, module)的回撥函式給define函式,就可以把需要注入的變數和函式注入到模組程式碼內。之前的例項程式碼,在這裡寫成:

檔案index.js

// index.js
define(function(require, exports, module) {
	var d = require('./dep1')
	console.log(d.dep1())
});
複製程式碼

檔案dep1.js

define(function(require, exports, module) {
	var d = require('./dep2')
	var v1 = "dep1"
	function dep1(){
		return v1+"-"+d.dep2()
	}
	exports.dep1 = dep1
});
複製程式碼

檔案dep2.js

define(function(require, exports, module) {
	var v2 = "dep2"
	function dep2(){
		return v2
	}
	exports.dep2 = dep2
});
複製程式碼

除了加上一層有點看起來莫名其妙的外套程式碼,其他的模組程式碼,你該怎麼寫就怎麼寫。倘若不是那麼潔癖,這樣的程式碼確實解決了之前使用script標籤載入程式碼帶來的全域性變數汙染等問題,並且還是可以非同步載入的,那些看起來不錯的依賴關係,也如Nodejs一樣。以上程式碼,可以直接把nodejs對應的程式碼拷貝過來,加上外套即可執行。

我們不妨加入seajs檔案,來看看實際的使用效果:

//index.html
<script type="text/javascript" src="https://cdn.bootcss.com/seajs/3.0.2/sea.js"></script>
<script>
  seajs.use('./index.js');
</script>
複製程式碼

這裡為了偷懶,我使用了seajs的CDN檔案。如果有遇到什麼問題,你不妨自己下載一個seajs檔案,然後改成你的URL即可。

載入此HTML檔案,可以在控制檯看到輸出:

dep1+dep2
複製程式碼

說明seajs執行效果不錯!

  1. seajs通過use方法來載入入口模組,可選接收一個回撥函式,當模組載入完成會呼叫此回撥函式,並傳入對應的模組作為引數
  2. 來獲取到模組後,等待模組(包括模組依賴的模組)載入完成會呼叫回撥函式。
  3. 分析模組的依賴,按依賴關係遞迴執行document.createElement(‘script’),這些標籤的建立會導致瀏覽器載入對應的指令碼

對模組的價值,都是非同步載入,瀏覽器不會失去響應,它指定的回撥函式,只有前面的模組都載入成功後,才會執行,解決了依賴性的問題。

可以在控制檯輸入:

seajs.data.fetchedList
複製程式碼

檢視檔案載入清單。

因為不是語言自帶,而是社群通過現有的語言特性,硬造出來的一個模組系統,因為看起來程式碼不免累贅。但是在沒有原生模組的情況下,這樣做確實是管用的。要知道真正的原生模組,在ES6標準之後才出現,這都是2015年的事兒了。在一些有名的應用如Gmail、Google Map的推動下,Web從簡單的展示到App的變化,迫切需要這樣類似的模組技術,大家等不了那麼久,先弄一個能用的是很重要的。

為什麼要套這層外殼呢?就是為了解決全域性變數汙染問題。在JavaScript語言內,唯一提供本地作用域的就是函式和閉包,通過閉包function(require, exports, module),模組載入器給模組注入了必要的函式和變數。看起來在模組之內的任何地方都可以使用require和exports,但是他沒都不是全域性變數,而是閉包內變數。這些變數都是區域性化的,絕對不會汙染全域性空間。

使用require函式,可以就近指定對其他模組的依賴,函式本身是由sea.js這樣的模組載入器提供,它會內部構造依賴關係圖譜,並根據依賴關係,設定加入script標籤的次序。

更加詳細的理解這層外殼,可以閱讀seajs原始碼,程式碼量並不大,值得一讀。或者看看此問答

當然Seajs也引入了自己的規範,叫做CMD規範。它的前身是Modules/Wrappings規範。SeaJS更多地來自 Modules/2.0 的觀點,同時借鑑了 RequireJS 的不少東西,比如將Modules/Wrappings規範裡的 module.declare改為define等。 說是規範,卻不像是一般的規範那麼冗長,可能列印出來也就一兩頁的紙張而已,這也是JavaScript社群的一個特點吧。Modules/Wrappings

seajs的作者在一篇文章中提到了業界在開發前端模組載入器時的場景:

大概 09 年 - 10 年期間,CommonJS 社群大牛雲集。CommonJS 原來叫 ServerJS,推出 Modules/1.0 規範後,在 Node.js 等環境下取得了很不錯的實踐。09年下半年這幫充滿幹勁的小夥子們想把 ServerJS 的成功經驗進一步推廣到瀏覽器端,於是將社群改名叫 CommonJS,同時激烈爭論 Modules 的下一版規範。分歧和衝突由此誕生,逐步形成了三大流派:

  1. Modules/1.x 流派。這個觀點覺得 1.x 規範已經夠用,只要移植到瀏覽器端就好。要做的是新增 Modules/Transport 規範,即在瀏覽器上執行前,先通過轉換工具將模組轉換為符合 Transport 規範的程式碼。主流代表是服務端的開發人員。現在值得關注的有兩個實現:越來越火的 component 和走在前沿的 es6 module transpiler。
  2. Modules/Async 流派。這個觀點覺得瀏覽器有自身的特徵,不應該直接用 Modules/1.x 規範。這個觀點下的典型代表是 AMD 規範及其實現 RequireJS。
  3. Modules/2.0 流派。這個觀點覺得瀏覽器有自身的特徵,不應該直接用 Modules/1.x 規範,但應該儘可能與 Modules/1.x 規範保持一致。這個觀點下的典型代表是 BravoJS 和 FlyScript 的作者。BravoJS 作者對 CommonJS 的社群的貢獻很大,這份 Modules/2.0-draft 規範花了很多心思。FlyScript 的作者提出了 Modules/Wrappings 規範,這規範是 CMD 規範的前身。可惜的是 BravoJS 太學院派,FlyScript 後來做了自我閹割,將整個網站(flyscript.org)下線了。這個故事有點悲壯,下文細說。

也談談require.js

這個模組載入器是更加主流的。之所以不是首先提到它,是因為概念上來說seajs更加簡明。和seajs相比,requirejs是更加主流的框架。它的差異主要是一些零零散散的不同,比如模組程式碼的外套是不太一樣的:

require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
複製程式碼

    // some code here });

匯出模組變數和函式的方式,也是不同的。requirejs的引出方式是直接返回:

return {foo:foo}
複製程式碼

一樣的案例,使用requirejs的話,程式碼是這樣的:

index.html檔案

<script data-main="index"
 src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js" ></script>
複製程式碼

index.js檔案:

require(['./dep1'], function (d){
	console.log(d.dep1())		
});
複製程式碼

dep1.js檔案:

define(['./dep2'], function (d){
	var v1 = "dep1"
	function dep1(){
		return v1+"-"+d.dep2()
	}
	return {dep1:dep1}
});
複製程式碼

dep2.js檔案:

define(function() {
	var v2 = "dep2"
	function dep2(){
		return v2
	}
	return {dep2:dep2}
});
複製程式碼

瀏覽器開啟檔案index.html,可以看到控制檯輸出一樣的結果。

稍加對比require.js和sea.js。使用Require.js,預設推薦的模組格式是:

define(['a','b'], function(a, b) {
  // do sth
})
複製程式碼

使用seajs的時候,類似的功能,程式碼這樣寫:

define(function(require, exports, module) {
  var a = require('a')
  var b = require('b')
  // do sth
  ...
})
複製程式碼

Seajs的做法是更加現代的。我需要用的時候,我才去引用它,而不是實現什麼都引用好,然後用的時候直接用就好。

Modules/1.x規範

以require.js為代表的Modules/Async流派,尊重了瀏覽器的特殊性,代價是不管寫什麼模組,都得自己給自己穿上一層外套,對於有程式碼潔癖的人來說,這樣的情況是看不下去的。最好是開發人員編寫乾乾淨淨的模組程式碼,框架開發者做一個工具,這個工具自動的把這些程式碼轉義成客戶端認可的非同步程式碼。即在瀏覽器上執行前,先通過轉換工具將模組轉換為符合規範的程式碼。這就是Modules/1.x 流派的做法。需要注意的是1.x和2.0還有Async流派不能簡單的認為版本號大的就更好。倒是理解成各自不同的解決方案為好。

以我們自己的案例來說,就是可以直接把nodejs程式碼那裡,使用一個工具做一個轉換,即可得到符合前端需要的程式碼,這些程式碼是非同步載入的、是可以保證模組變數區域性化的、是可以由良好的依賴關係定義的。工具browerfy就是做這個的。我們來試試具體是怎麼玩的。

首先安裝此工具:

npm install --global browserify
複製程式碼

到你的nodejs程式碼內,然後轉換此程式碼,生成一個新的js檔案,一般命名為bundle.js:

browserify index.js -o bundle.js
複製程式碼

然後建立index.html並引入bundle.js:

<script type="text/javascript" src="./bundle.js"></script>
複製程式碼

使用瀏覽器開啟此HTML檔案,可以在控制檯看到熟悉的輸出,這說明轉換是有效的:

dep1+dep2
複製程式碼

本身nodejs的程式碼,是不能在瀏覽器執行的,瀏覽器內也沒有什麼require函式,但是轉換後就可以執行了。那麼,轉換的過程,到底玩了什麼魔術?

像是browserify這樣的工具,就是找到全面被引入的程式碼,解析它的依賴關係,並且自動的加入我們在requirejs裡面需要的外套程式碼。儘管bundle.js檔案並不是為了閱讀優化的,但是可以取出其中的程式碼片段來證實我們的觀點:

{"./dep2":2}],2:[function(require,module,exports){
		var v2 = "dep2"
		function dep2(){
			return v2
		}
		exports.dep2 = dep2
},{}],3:[function(require,module,exports){
		var d = require('./dep1')
		console.log(d.dep1())
},{"./dep1":1}]},{},[3]);
複製程式碼

我們可以看到本來的nodejs程式碼,以及它們對應的外套。還是比較簡單,就不進一步解釋了。browserify不但完成了加外套程式碼的工作,還同時把若干小檔案打成一個大的檔案,對於當前使用的HTTP主流版本1.1來說,這樣做會讓載入效率更高。但是對於HTTP/2.0來說,它已經支援了多個檔案在一個連線內交錯傳遞,因此再做打包的意義就不大了。只是...HTTP/2.0的普及還需要時日。

browerify完成的工作簡明而單一。另外一個主流的同類工具叫做webpack,不但可以轉換js程式碼,還可以打包css檔案、圖片檔案,並且可以做一些工程化的管理,代價就是webpack學起來也困難的多。實際上像是Vuejs這樣的UI開發框架,內部就是使用了webpack做工程化管理和程式碼轉譯的。但是在模組化方面,兩者是差不多的。就不另外介紹了。

ES6 Module

時間到了May 9, 2018,我看到了阮一峰釋出了這樣的微博:

今天 Firefox 60釋出,預設開啟了ES6 模組支援,至此所有瀏覽器都預設支援ES6模組。前端開發模式可能因此大變。現在的方案是所有模組發到npm,本地寫好入口檔案,再用webpack打包成一個指令碼。但是如果瀏覽器原生支援,為什麼還要打包呢?至少簡單的應用可以直接載入入口檔案,瀏覽器自己去抓取依賴。 ​​​​
複製程式碼

這裡所有瀏覽器指的是Edge、Firefox、Chrome、Safari。當然,再一次沒有IE。如果想要支援IE或者比較老的版本的話,還是需要使用打包器來完成程式碼的轉譯。另外很多人表示會繼續使用Webpack,原因很簡單,Webpack不僅僅是完成模組打包工作,還有壓縮、混淆等,並且很多框架還需要依賴它。所以遷移並非一朝一夕之功。而無需考慮老版本瀏覽器的相容的程式碼,是完全可以大量的使用它了。了不起在把Webpack加起來轉換ES Module到加外套的程式碼就是了。

ES6 Module不是requirejs那樣加外套的樣子,也不是Nodejs使用require函式的樣子,而是另外一套有官方提出的模組模式。它使用的是import、export關鍵字。官方的就是不一樣,社群是加不了關鍵字的。同樣的案例,使用ES6 Module就是這樣的了。

index.html檔案:

<script type="module">
		import {dep1 } from './dep1.js'
		console.log(dep1())
</script>
複製程式碼

dep1.js檔案

	import {dep2} from './dep2.js'
	var v1 = "dep1"
	export function dep1(){
		return v1+"-"+dep2()
	}
複製程式碼

dep2.js檔案:

	var v2 = "dep2"
	export function dep2(){
		return v2
	}
複製程式碼

ES6 Module要求必須有後臺的HTTP伺服器,而不能直接在檔案系統上完成Demo的測試。所幸使用Nodejs搭建一個伺服器也非常簡單直接:

npm i http-server -g
http-server
複製程式碼

在瀏覽器內訪問此HTML檔案的URL,可以看到控制檯輸出:dep1+dep2。這個輸出,已經是你的老朋友了。

Nodejs在10.9才支援實驗版本的ES6 Module,是落後了點,但是對於Nodejs來說,新的模組技術本來也就並不迫切。

最佳實踐建議

綜合以上的內容,我認為,在不必考慮古老的瀏覽器相容的情況下,最好的實踐是這樣的:

  1. 直接使用ES6 Module編寫模組程式碼
  2. 使用Rollup清除沒有呼叫的程式碼,降低程式碼的大小
  3. 使用Ugly工具壓縮和混淆程式碼
  4. 使用HTTP/2做網路傳遞協議

這樣的實踐,會隨著HTTP/2的逐步普及和ES6被更多的開發者採用,而成為更好的選擇。

使用ES6 Module的壞處是無法像require那樣動態的載入。但是好處是可以精確指明對於一個庫,我們使用的是那些,這就給工具提供了優化的可能,就是說如果我引入了一個庫,但是這個庫內有些我不會用的,那麼這些不會被用到的程式碼也不會載入到前端了。這個功能對於後端來說意義不大,但是對於前端來說,就是非常令人喜歡的功能了。實際上,這樣的工具已經有了,比較知名的就是rollup,它屬於了一種被稱為tree-shaking的技術優化使用程式碼。

而以往做模組打包,很多的原因是HTTP/1.1傳遞大量小檔案的時候開銷比較大,而打包成單一的問題,就可以更好的利用HTTP/1.1的傳輸特性。但是HTTP/2.0的一個大的特色就是可以在單一的連線內,併發和交錯的傳遞多個流,因為在一個連線內交錯的傳遞多個檔案,就可以不再有HTTP/1.1的連線開銷了。因此,在HTTP/2.0被採納的網路裡面,打包單一檔案的價值幾乎沒有了。直接使用小檔案預設情況下就可以得到比較好的優化傳輸。

按照現在的技術發展的勢頭,要不了幾年,打包器將不再那麼必要,使用原生程式碼編寫模組將會成為主流的。

參考

參考文章不少,其中模組歷史和選型如下:

  1. 前端模組化開發那點歷史
  2. 梳理的還是比較清晰
  3. 有點黑客精神的小夥伴,玩的很廣譜
  4. 介紹Bower
  5. npm for Beginners: A Guide for Front-end Developers
  6. Es6module 出來了,是否應該重新考慮打包的方案?

未來

這篇文章預計想要編寫的YUI方法,YUI Combo方法,想了想還是算了,因為這樣的恐龍程式碼,已經在日常的程式碼實踐中逐步消失,作為一個曾經比較重要,現在則退居二線的程式碼庫,對它最好的讚許就是讓它退休,也不必給讀者增加額外的閱讀負擔了。畢竟require.js、browerify、webpack都工作的不錯,在此基礎上發展的Vuejs、React.js也的得到了更多的認可。

本文講到的模組規範和實踐工具,為編寫一個廣為社群認可的模組起到了最基礎的規範作用。但是,JavaScript社群最為令人稱道的就是程式碼庫倉庫。包括NPM倉庫,Bower倉庫。在這些倉庫內,有模組依賴管理工具,還有工程化工具。這些內容,它們當然是重要的,不在本文的範圍內。

作為前端開發者,有人採用Bower管理元件依賴,也有人使用Npm做類似的工作。有很多時候,這樣的實踐是令人困惑的。還有這裡npm and the front end,NPM官方也對npm在前端的使用,提出了[自己的看法][blog.npmjs.org/post/101775…]。

這些未盡的內容,或許在未來的文章中表達之。

相關文章