【相關推薦:、】
隨著前端功能越來越複雜,前端程式碼日益膨脹,為了減少維護成本,提高程式碼的可複用性,前端模組化勢在必行。
所有js檔案都在一個html中引入,造成以下不良影響:
請求過多。首先我們要依賴多個模組,那樣就會傳送多個請求,導致請求過多
依賴模糊。我們不知道他們的具體依賴關係是什麼,也就是說很容易因為不瞭解他們之間的依賴關係導致載入先後順序出錯。
難以維護。以上兩種原因就導致了很難維護,很可能出現牽一髮而動全身的情況導致專案出現嚴重的問題。
webpack中是這樣定義的:
在模組化程式設計中,開發者將程式分解成離散功能塊(discrete chunks of functionality),並稱之為模組。 每個模組具有比完整程式更小的接觸面,使得校驗、偵錯、測試輕而易舉。 精心編寫的模組提供了可靠的抽象和封裝界限,使得應用程式中每個模組都具有條理清楚的設計和明確的目的。
模組應該是職責單一、相互獨立、低耦合的、高度內聚且可替換的離散功能塊。
模組化的概念
模組化是一種處理複雜系統分解成為更好的可管理模組的方式,它可以把系統程式碼劃分為一系列職責單一,高度解耦且可替換的模組,系統中某一部分的變化將如何影響其它部分就會變得顯而易見,系統的可維護性更加簡單易得。
模組化是一種分治的思想,通過分解複雜系統為獨立的模組實現細粒度的精細控制,對於複雜系統的維護和管理十分有益。模組化也是元件化的基石,是構成現在色彩斑斕的前端世界的前提條件。
為什麼需要模組化
前端開發和其他開發工作的主要區別,首先是前端是基於多語言、多層次的編碼和組織工作,其次前端產品的交付是基於瀏覽器,這些資源是通過增量載入的方式執行到瀏覽器端,如何在開發環境組織好這些碎片化的程式碼和資源,並且保證他們在瀏覽器端快速、優雅的載入和更新,就需要一個模組化系統。
可維護性。 因為模組是獨立的,一個設計良好的模組會讓外面的程式碼對自己的依賴越少越好,這樣自己就可以獨立去更新和改進。
名稱空間。 在 JavaScript 裡面,如果一個變數在最頂級的函數之外宣告,它就直接變成全域性可用。因此,常常不小心出現命名衝突的情況。使用模組化開發來封裝變數,可以避免汙染全域性環境。
重用程式碼。 我們有時候會喜歡從之前寫過的專案中拷貝程式碼到新的專案,這沒有問題,但是更好的方法是,通過模組參照的方式,來避免重複的程式碼庫。我們可以在更新了模組之後,讓參照了該模組的所有專案都同步更新,還能指定版本號,避免 API 變更帶來的麻煩。
1. 最簡單粗暴的方式
function fn1(){ // ... } function fn2(){ // ... }
通過 script 標籤引入檔案,呼叫相關的函數。這樣需要手動去管理依賴順序,容易造成命名衝突,汙染全域性,隨著專案的複雜度增加維護成本也越來越高。
2. 用物件來模擬名稱空間
var output = { _count: 0, fn1: function(){ // ... } }
這樣可以解決上面的全域性汙染的問題,有那麼點名稱空間的意思,但是隨著專案複雜度增加需要越來越多的這樣的物件需要維護,不說別的,取名字都是個問題。最關鍵的還是內部的屬性還是可以被直接存取和修改。
3. 閉包
var module = (function(){ var _count = 0; var fn1 = function (){ // ... } var fn2 = function fn2(){ // ... } return { fn1: fn1, fn2: fn2 } })() module.fn1(); module._count; // undefined
這樣就擁有獨立的詞法作用域,記憶體中只會存在一份 copy。這不僅避免了外界存取此 IIFE 中的變數,而且又不會汙染全域性作用域,通過 return 暴露出公共介面供外界呼叫。這其實就是現代模組化實現的基礎。
4. 更多
還有基於閉包實現的鬆耦合拓展、緊耦合拓展、繼承、子模組、跨檔案共用私有物件、基於 new 構造的各種方式,這種方式在現在看來都不再優雅。
// 鬆耦合拓展 // 這種方式使得可以在不同的檔案中以相同結構共同實現一個功能塊,且不用考慮在引入這些檔案時候的順序問題。 // 缺點是沒辦法重寫你的一些屬性或者函數,也不能在初始化的時候就是用module的屬性。 var module = (function(my){ // ... return my })(module || {}) // 緊耦合拓展(沒有傳預設引數) // 載入順序不再自由,但是可以過載 var module = (function(my){ var old = my.someOldFunc my.someOldFunc = function(){ // 過載方法,依然可通過old呼叫舊的方法... } return my })(module)
歷史上,JavaScript 一直沒有模組(module)體系,無法將一個大程式拆分成互相依賴的小檔案,再用簡單的方法拼裝起來。其他語言都有這項功能,比如 Ruby 的require、Python 的import,甚至就連 CSS 都有@import,但是 JavaScript 任何這方面的支援都沒有,這對開發大型的、複雜的專案形成了巨大障礙。
在 ES6 之前,社群制定了一些模組載入方案,最主要的有 CommonJS 和 AMD 兩種。前者用於伺服器,後者用於瀏覽器。ES6 在語言標準的層面上,實現了模組功能,而且實現得相當簡單,完全可以取代 CommonJS 和 AMD 規範,成為瀏覽器和伺服器通用的模組解決方案。
ES6 模組的設計思想是儘量的靜態化,使得編譯時就能確定模組的依賴關係,以及輸入和輸出的變數。CommonJS 和 AMD 模組,都只能在執行時確定這些東西。比如,CommonJS 模組就是物件,輸入時必須查詢物件屬性。
目前實現模組化的規範主要有:
CommonJS
CommonJS 是以在瀏覽器環境之外構建 JavaScript 生態系統為目標而產生的專案,比如在伺服器和桌面環境中。
採用同步載入模組的方式,也就是說只有載入完成,才能執行後面的操作。CommonJS 代表:Node 應用中的模組,通俗的說就是你用 npm 安裝的模組。
它使用 require 參照和載入模組,exports 定義和匯出模組,module 標識模組。使用 require 時需要去讀取並執行該檔案,然後返回 exports 匯出的內容。
CMD
CMD(Common Module Definition)
CMD是SeaJS在推廣過程中生產的對模組定義的規範,在Web瀏覽器端的模組載入器中,SeaJS與RequireJS並稱,SeaJS作者為阿里的玉伯。
CMD規範專門用於瀏覽器端,模組的載入是非同步的,模組使用時才會載入執行。CMD規範整合了CommonJS和AMD規範的特點。在 Sea.js 中,所有 JavaScript 模組都遵循 CMD模組定義規範。
AMD
AMD(Asynchronous Module Definition)
非同步模組定義,所謂非同步是指模組和模組的依賴可以被非同步載入,他們的載入不會影響它後面語句的執行。有效避免了採用同步載入方式中導致的頁面假死現象。AMD代表:RequireJS。
AMD一開始是CommonJS規範中的一個草案,全稱是Asynchronous Module Definition,即非同步模組載入機制。後來由該草案的作者以RequireJS實現了AMD規範,所以一般說AMD也是指RequireJS。
RequireJS是一個工具庫,主要用於使用者端的模組管理。它的模組管理遵守AMD規範,RequireJS的基本思想是,通過define方法,將程式碼定義為模組;通過require方法,實現程式碼的模組載入。
ES6模組
ES6模組的設計思想,是儘量的靜態化,使得編譯時就能確定模組的依賴關係,以及輸入和輸出的變數。所以說ES6是編譯時載入,不同於CommonJS的執行時載入(實際載入的是一整個物件),ES6模組不是物件,而是通過export命令顯式指定輸出的程式碼,輸入時也採用靜態命令的形式。
ES6 的模組自動採用嚴格模式,不管你有沒有在模組頭部加上」use strict」;。
嚴格模式主要有以下限制。
變數必須宣告後再使用
函數的引數不能有同名屬性,否則報錯
不能使用with語句
不能對唯讀屬性賦值,否則報錯
不能使用字首 0 表示八進位制數,否則報錯
不能刪除不可刪除的屬性,否則報錯
不能刪除變數delete prop,會報錯,只能刪除屬性delete global[prop]
eval不會在它的外層作用域引入變數
eval和arguments不能被重新賦值
arguments不會自動反映函數引數的變化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全域性物件
不能使用fn.caller和fn.arguments獲取函數呼叫的堆疊
增加了保留字(比如protected、static和interface)
上面這些限制,模組都必須遵守。
【相關推薦:、】
以上就是Module模組化程式設計的優點(總結分享)的詳細內容,更多請關注TW511.COM其它相關文章!