好程式設計師web前端教程之前端模組化開發
1. 命名衝突
首先從一個簡單的習慣開始。
由於以前一直做 JavaEE 開發的緣故,在 JavaScript 開發中,我已經習慣將專案中的一些通用功能抽象出來,形成一個個的獨立函式,以便於實現程式碼複用,如:
function css(element, attr) { // 獲取 element 元素的 attr 對應的 CSS 屬性值
// ...
}
function offset(element) { // 獲取 element 元素在文件中的位置座標
// ...
}
並把這些封裝的函式放在統一的 tools.js 檔案中。
如果頁面功能實現需要使用到這些函式,則直接透過 引入即可。
前期感覺一切都好,大家也都覺得寫這樣的工具檔案對開發來說很方便,直到使用越來越多,頁面功能越來越複雜,大家要實現的需求也越來越多樣。
這時有人就抱怨,因為引入了 tools.js 檔案,如果要定義一個能夠設定 css 屬性值的函式,那麼就只有取另外的函式名稱(如 setCss )而不能再使用 css 這個函式名稱了,同樣如果要設定一個元素在整個文件中的定位座標,也不能再使用 offset 這個函式名稱,因為那樣的話,就與 tools.js 檔案中已定義的函式名稱衝突了。
既然問題出現了,就需要解決。
在 Java 中有一個非常實用的技術——package,它將邏輯上相關的程式碼組織在一起使用“包”來進行管理,這相當於檔案系統中的資料夾。在檔案系統中,資料夾內是相對獨立的一個空間,不用擔心一個資料夾和另一個資料夾中檔案命名的衝突。在“包”中也一樣,可以解決檔案命名衝突問題,如果要在包外部再使用到包內的資源,直接透過 import 匯入相關的 package 即可。類似包這樣的概念,在其它的語言(如 C#)中也稱為名稱空間。
JavaScript 中並沒有提供原生的包或名稱空間的支援,但可以使用其它的方法(如物件、閉包)來實現類似的效果。
參照 Java 的方式,我使用 JavaScript 中的物件來簡單修改 tools.js 檔案:
var Util = {
css : function(element, attr) {
// ...
},
offset : function(element) {
// ...
}
};
這樣,當引入 tools.js 檔案後,要獲取 CSS 樣式或獲取元素的文件座標,就透過類似 Util.css()/Util.offset() 的方法來實現。css 與 offset 的作用域是在物件 Util 下,再全域性或是新物件中定義 css 屬性是不受影響的。
Util 這個名稱也具有通用性,通常用作輔助工具定義的時候會使用到這個名稱,為了體現該名稱的唯一性,可以繼續借鑑 Java 中 package 的命名規範(域名倒置):
var com = {};
com.github = {};
com.github.itrainhub = {};
com.github.itrainhub.Util = {
css : function(element, attr) {
// ...
},
offset : function(element) {
// ...
}
};
要獲取 CSS 樣式值,則可使用 com.github.itrainhub.Util.css() 方法。但這樣的寫法增加了記憶難度,YUI 中關於這一點有比較好的解決方案,先按下暫且不表。
使用物件的寫法可解決命名衝突問題,但這種寫法也會暴露物件的所有成員,使物件內部狀態可以在物件外部被改寫。比如在物件內部存在計數器:
var Util = {
_count : 0
}
在物件外部可以透過 Util._count = 18; 修改該計數器的值,這是不安全的。
像計數器這樣的變數,通常可能是作為物件的私有成員存在,不希望在物件外部還能繼續修改其值,這時,可以使用 IIFE(立即執行函式)來設計:
var Util = (function(){
var _count = 0;
function _css(element, attr) {
// ...
}
function _offset(element) {
// ...
}
return {
css : _css,
offset : _offset
}
})();
這樣,在外部就不能再直接修改 _count 的值了。
透過名稱空間,的確可以解決命名衝突的問題,我們可以暫時鬆一口氣了。
2. 檔案依賴
接著 tools.js 繼續開發。
在 tools.js 的基礎上,可以開發出一些 UI 層通用的元件,如放大鏡、輪播圖之類的,這樣各個專案中要使用這些功能的時候就不用重複造輪子了。
通常情況下,每個 UI 元件都是以獨立的 js 檔案存在的,比如放大鏡,可以將它放到一個 zoom.js 的檔案中,當要使用到放大鏡元件時,透過 引入即可。
但很多時候,在使用 zoom.js 之前忘記了引入 tools.js,則使用 zoom.js 就會報錯,無法保證它的正常執行。
zoom.js 的正常執行依賴於 tools.js 的使用,上述的問題都還是比較容易解決的,但隨著團隊越來越大,業務需求越來越複雜,專案中元件間的依賴關係也會變得越來越複雜。比如:
某一天,我擴充了 zoom.js 元件的功能,但除了使用到 tools.js 外,還使用到另一個工具 js 元件:helper.js。如果專案中已有 N 個地方之前使用到了 zoom.js 元件,我就只好全域性搜尋每個引用 zoom.js 的地方,再加上對 helper.js 的引用。
再想想,隨著專案推進,我們會繼續修改 tools.js,新增更多的元件 component_1.js、component_2.js……某些元件中只使用到 tools.js,某些只使用到 helper.js,而某些元件既使用到了 tools.js 又使用到了 helper.js。那麼關於元件間依賴關係的維護,工作量可想而知,如果以人肉的方式來保證依賴關係的維護,簡直就要崩潰掉了。
為什麼維護元件間的依賴關係這麼費神呢,因為 JavaScript 中天生缺少了引入其它 js 檔案的語法。在 Java 中可以透過 import 引入依賴元件,在 CSS 中也有 @import 命令去引入其它的 CSS 檔案,而 js 中卻不能自動管理依賴。
除了檔案間的依賴關係維護不便外,如果在頁面中引入的元件非常多,我們還得保證引用元件的路徑及先後順序不能出錯,一旦出錯,又得花時間查詢錯誤,可想而知工作量是很可觀了,再加上元件引入過多,又是以同步的方式載入各元件,也可能導致瀏覽器假死的現象。
要解決這些問題,模組化開發的價值就體現出來了。
3. 模組化開發
3.1 模組化
所謂模組化,就是把一個相對獨立的功能,單獨形成一個檔案,可輸入指定依賴、輸出指定的函式,供外界呼叫,其它都在內部隱藏實現細節。這樣即可方便不同的專案重複使用,也不會對專案造成額外的影響。
前端使用模組化載發主要的作用是:
• 非同步載入 js,避免瀏覽器假死
• 管理模組間依賴關係,便於模組的維護
有了模組,我們就可以更方便地使用別人的程式碼,想要什麼功能,就載入什麼模組。
但要使用模組的前提,是必然要形成可遵循的開發規範,使得開發者和使用者都有據可尋,否則你有你的寫法,我有我的寫法,大家沒辦法統一,也就不能很好的互用了。
目前通用的規範是,伺服器端使用 CommonJS 規範,客戶端使用 AMD/CMD 規範。
3.2 CommonJS
CommonJS 規範出現是在 2009 年,Node.js 就是該規範的實現。CommonJS 規範中是這樣載入模組的:
var gulp = require("gulp");
gulp.task(/* 任務 */);
模組的載入是同步的,這種寫法適合伺服器端,因為在伺服器讀取的模組都是在本地磁碟,載入速度很快,可同步載入完成。但是如果在客戶端瀏覽器中,因為模組是放在伺服器端的,模組載入取決於網路環境,以同步的方式載入模組時有可能出現 “假死”狀況。
今天我主要介紹針對瀏覽器程式設計,不針對 Node.js 內容,所以在此關於 CommonJS 規範就不作深究,知道 require() 用於載入模組即可。
3.3 AMD
由於在瀏覽器端,模組使用同步方式載入可能出現假死,那麼我們採用非同步載入的方式來實現模組載入,這就誕生了 AMD 的規範。
AMD 即 Asynchronous Module Definition 的簡稱,表示“非同步模組定義”的意思。AMD 規範:。
AMD 採用非同步方式載入模組,模組的載入不影響它後面語句的執行。所有依賴所載入模組的語句,都被定義在一個回撥函式中,等到模組載入完畢後,回撥函式才會執行。
AMD 也採用 require() 來載入模組,語法結構為:
require([module], callback);
module 是陣列引數,表示所載入模組的名稱;callback 是回撥函式引數,所有模組載入完畢後執行該回撥函式。如:
require(["jquery"], function($){
$("#box").text("test");
});
3.4 CMD
CMD 即 Common Module Definition 的簡稱,表示“通用模組定義”的意思。CMD 規範:。
CMD 規範明確了模組的基本書寫格式和基本互動規則,該規範是在國內發展出來的,由玉伯在推廣 SeaJS 過程中規範產出的。
SeaJS 實現了 CMD 規範。SeaJS 要解決的問題和 RequireJS 一樣,只不過在模組定義方式和模組載入(執行、解析)時機上有所不同。
3.5 AMD 與 CMD 的區別
AMD 是 RequireJS 在推廣過程中對模組定義的規範化產出。
CMD 是 SeaJS 在推廣過程中對模組定義的規範化產出。
二者主要區別如下:
1 對於依賴的模組,AMD 是提前執行,CMD 是延遲執行。
2 CMD 推崇依賴就近,AMD 推崇依賴前置。
3 AMD 的 API 預設是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一。
當然還有一些其它細節上的區別,具體看規範的定義就好。
本文由 好 程式設計師 web前端培訓 學員總結。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913892/viewspace-2639602/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 好程式設計師web前端教程分享js中的模組化二程式設計師Web前端JS
- 好程式設計師web前端教程分享js中的模組化一程式設計師Web前端JS
- 好程式設計師Web前端教程分享JavaScript開發技巧程式設計師Web前端JavaScript
- 好程式設計師web前端乾貨之web前端開發框架彙總程式設計師Web前端框架
- 好程式設計師web前端教程:字串程式設計師Web前端字串
- 好程式設計師web前端教程JavaScript系列之HTTP程式設計師Web前端JavaScriptHTTP
- 好程式設計師分享Web前端開發工具程式設計師Web前端
- 好程式設計師web前端開發測驗之css部分程式設計師Web前端CSS
- 好程式設計師web前端教程分享前端javascript練習題之promise程式設計師Web前端JavaScriptPromise
- 好程式設計師web前端分享移動前端開發和web前端開發的區別程式設計師Web前端
- 好程式設計師web前端教程分享web前端基礎知識程式設計師Web前端
- 好程式設計師web前端教程:Math函式程式設計師Web前端函式
- 好程式設計師web前端教程分享CSS技巧!程式設計師Web前端CSS
- 好程式設計師web前端教程分享前端javascript練習題之閉包案例程式設計師Web前端JavaScript
- 好程式設計師web前端教程分享前端javascript練習題二程式設計師Web前端JavaScript
- 好程式設計師web前端教程分享前端javascript練習題三程式設計師Web前端JavaScript
- 好程式設計師web前端教程分享前端 javascript 練習題二程式設計師Web前端JavaScript
- 好程式設計師Web前端分享前端CSS篇程式設計師Web前端CSS
- 好程式設計師web前端教程分享web前端入門基礎知識程式設計師Web前端
- 好程式設計師web前端教程分享JavaScript面試題程式設計師Web前端JavaScript面試題
- 好程式設計師web前端教程分享js閉包程式設計師Web前端JS
- 好程式設計師web前端教程分享js模板模式程式設計師Web前端JS模式
- 好程式設計師web前端教程分享js reduce方法使用教程程式設計師Web前端JS
- 好程式設計師分享Web前端開發就業前景如何?程式設計師Web前端就業
- 好程式設計師分享Web前端效能最佳化程式設計師Web前端
- 好程式設計師分享Web前端知識之HTML程式設計師Web前端HTML
- 好程式設計師web前端分享應該怎樣學好web前端?程式設計師Web前端
- 好程式設計師web前端教程分享javascript 練習題程式設計師Web前端JavaScript
- 好程式設計師web前端教程分享JavaScript簡寫方法程式設計師Web前端JavaScript
- 好程式設計師Web前端教程分享Vue學習心得程式設計師Web前端Vue
- 好程式設計師web前端教程之Node.Js流程程式設計師Web前端Node.js
- 好程式設計師web前端分享web前端入門知識程式設計師Web前端
- 好程式設計師web前端精講 web前端三要素程式設計師Web前端
- 好程式設計師web前端分享Nodejs學習筆記之Stream模組程式設計師Web前端NodeJS筆記
- 好程式設計師web前端分享:如何理解web語義化?程式設計師Web前端
- 好程式設計師web前端教程分享web中CSS絕對定位程式設計師Web前端CSS
- 好程式設計師web前端教程分享前端javascript練習題Ajax封裝程式設計師Web前端JavaScript封裝
- 好程式設計師web前端教程分享前端三大框架有哪些異同程式設計師Web前端框架