隨著前端的發展,無模組化存在的問題日益顯露。但在這個過程中,模組化也是有一定的發展。
stage1-檔案劃分方式
根據業務或功能封裝某一類變數或者函式;
約定每一個檔案都是一個模組;
當使用到這個模組的時候,通過script標籤引入到html頁面中,直接使用模組中的成員(變數|函式);
但當程式碼體積達到一定量的時候,這種方法的缺點就很明顯了。
//module-a.js // module a 相關狀態資料和功能函式 var name = 'module-a' function method1 () { console.log(name + '#method1') } function method2 () { console.log(name + '#method2') }
//module-b.js // module b 相關狀態資料和功能函式 var name = 'module-b' function method1 () { console.log(name + '#method1') } function method2 () { console.log(name + '#method2') }
//index.html <script src="module-a.js"></script> <script src="module-b.js"></script> <script> // 命名衝突 method1() // 模組成員可以被修改 name = 'foo' </script>
缺點:
- 全部變數都暴露在全域性作用域中,沒有私有空間,所有成員都可被外部訪問或者修改
- 當模組達到一定數量,通過約定的方式很難避免變數衝突
- 無法管理模組與模組之間的依賴關係
stage2-名稱空間的方式
每個模組只暴露一個全域性物件,所有的成員都掛載到這個全域性物件上
//module-a.js var moduleA = { name: 'module-a', method1: function () { console.log(this.name + '#method1') }, method2: function () { console.log(this.name + '#method2') } }
//module-b.js var moduleB = { name: 'module-b', method1: function () { console.log(this.name + '#method1') }, method2: function () { console.log(this.name + '#method2') } }
//index.html <script src="module-a.js"></script> <script src="module-b.js"></script> <script> moduleA.method1() moduleB.method1() // 模組成員可以被修改 moduleA.name = 'foo' </script>
優點:
通過“名稱空間”減小了命名衝突的可能
缺點:
模組內部的成員依然在外部可以被訪問和修改
模組之間的依賴關係依然不明確
stage3-通過立即執行函式(IIFE)為模組提供私有空間
具體做法就是將模組內的程式碼放在一個立即執行函式中
若想把某個成員暴露出去,就把該成員掛載到window中
//module-a.js ;(function () { var name = 'module-a' function method1 () { console.log(name + '#method1') } function method2 () { console.log(name + '#method2') } window.moduleA = { method1: method1, method2: method2 } })()
//module-b.js ;(function ($) { var name = 'module-b' function method1 () { console.log(name + '#method1') } function method2 () { console.log(name + '#method2') } window.moduleB = { method1: method1, method2: method2 }
console.log($('#a').html()); })(jQuery)
//index.html <script src="module-a.js"></script> <script src="module-b.js"></script> <script> moduleA.method1() moduleB.method1() // 模組私有成員無法訪問 console.log(moduleA.name) // => undefined </script>
優點:
實現了成員私有化,模組內的變數外界不能隨意訪問
通過立即執行函式,可以傳遞該模組所依賴的模組,使模組對外部的依賴更加清晰
小結:綜上所述,隨著模組化的發展,之前所提到的問題也逐一被解決,但還是存在一部分沒有解決的問題。先前所有模組都是通過script標籤引入的。若我們需要某個模組時,通過script標籤引入了該模組,但可能會忘記引入該模組所依賴的模組。或者當我們刪除某個模組時,我們也可能會忘記該模組所依賴的模組。
stage4-模組化規範的出現
1.CommonJS
特點:
1.一個檔案就是一個模組
2.每個模組都有一個單獨的作用域
3.每個模組暴露出去的成員都由module.exports匯出
4.使用其他模組的成員時使用require函式匯入
CommonJS規範主要引用在Node專案中,該規範載入模組時同步載入,倘若用在瀏覽器中,會有大量的同步請求導致應用比較慢,但在Node中不會有問題(因為Node是啟動時載入模組)
2.AMD規範(Asynchronous Module Definition)
代表庫:require.js
特點:
不同於CommonJS的同步載入,該模組可以非同步載入模組,提高程式在瀏覽器的執行效率
但使用起來相對複雜
3.CMD規範( Common Module Definition )
代表庫:Sea.js
特點:
使用上類似於require.js,寫法類似於CommonJS,算是個重複的輪子
4.ES Modules(ES2015的新語法)
特點:
語言層面推出的新語法,比較完善
滿足在web端開發使用模組化的需求
綜上所述:目前前端模組化基本統一成了CommonJS(Node.js)和ES Modules(瀏覽器端開發),我們只需掌握這兩種規範開發即可,後續會對著兩個模組重點展開討論