1.概念
將一個復雜的程序依據一定的規則(規范)封裝成幾個塊(文件), 並進行組合在一起
塊的內部數據與實現是私有的, 只是向外部暴露一些接口(方法)與外部其它模塊通信
2.模塊化的發展進程
1.全局function模式 : 將不同的功能封裝成不同的全局函數
編碼: 將不同的功能封裝成不同的全局函數
問題: 污染全局命名空間, 容易引起命名沖突或數據不安全,而且模塊成員之間看不出直接關系
2.name space模式 : 簡單對象封裝
作用: 減少了全局變量,解決命名沖突
問題: 數據不安全(外部可以直接修改模塊內部的數據)
let myModule = { data: 'www.baidu.com', foo() { console.log(`foo() ${this.data}`) }, bar() { console.log(`bar() ${this.data}`) } } myModule.data = 'other data' //能直接修改模塊內部的數據 myModule.foo() // foo() other data
這樣寫會暴露所有模塊成員,內部狀態可以被外部改寫
3.IIFE模式:匿名函數自調用(閉包)
作用: 數據是私有的, 外部只能通過暴露的方法操作
編碼: 將數據和行為封裝到一個函數內部, 通過給window添加屬性來向外暴露接口
問題: 如果當前這個模塊依賴另一個模塊怎么辦?
// index.html文件 <script type="text/javascript" src="module.js"></script> <script type="text/javascript"> myModule.foo() myModule.bar() console.log(myModule.data) //undefined 不能訪問模塊內部數據 myModule.data = 'xxxx' //不是修改的模塊內部的data myModule.foo() //沒有改變 </script> // module.js文件 (function(window) { let data = 'www.baidu.com' //操作數據的函數 function foo() { //用於暴露有函數 console.log(`foo() ${data}`) } function bar() { //用於暴露有函數 console.log(`bar() ${data}`) otherFun() //內部調用 } function otherFun() { //內部私有的函數 console.log('otherFun()') } //暴露行為 window.myModule = { foo, bar } //ES6寫法 })(window)
最后結果:
4.IIFE模式增強 : 引入依賴
// module.js文件 (function(window, $) { let data = 'www.baidu.com' //操作數據的函數 function foo() { //用於暴露有函數 console.log(`foo() ${data}`) $('body').css('background', 'red') } function bar() { //用於暴露有函數 console.log(`bar() ${data}`) otherFun() //內部調用 } function otherFun() { //內部私有的函數 console.log('otherFun()') } //暴露行為 window.myModule = { foo, bar } })(window, jQuery) // index.html文件 <!-- 引入的js必須有一定順序 --> <script type="text/javascript" src="jquery-1.10.1.js"></script> <script type="text/javascript" src="module.js"></script> <script type="text/javascript"> myModule.foo() </script>
通過jquery方法將頁面的背景顏色改成紅色,所以必須先引入jQuery庫,就把這個庫當作參數傳入。這樣做除了保證模塊的獨立性,還使得模塊之間的依賴關系變得明顯。
3.模塊化好處
避免命名沖突(減少命名空間污染) 更好的分離, 按需加載 更高復用性 高可維護性
4.引入多個<script>
后出現出現問題
1.請求過多
首先我們要依賴多個模塊,那樣就會發送多個請求,導致請求過多
2.依賴模糊
我們不知道他們的具體依賴關系是什么,也就是說很容易因為不了解他們之間的依賴關系導致加載先后順序出錯。
3.難以維護
以上兩種原因就導致了很難維護,很可能出現牽一發而動全身的情況導致項目出現嚴重的問題。
模塊化固然有多個好處,然而一個頁面需要引入多個js文件,就會出現以上這些問題。而這些問題可以通過模塊化規范來解決。
5.模塊化規范
1.概述
Node 應用由模塊組成,采用 CommonJS 模塊規范。
每個文件就是一個模塊,有自己的作用域。
在一個文件里面定義的變量、函數、類,都是私有的,對其他文件不可見。
在服務器端,模塊的加載是運行時同步加載的;在瀏覽器端,模塊需要提前編譯打包處理。
2.特點
所有代碼都運行在模塊作用域,不會污染全局作用域。
模塊可以多次加載,但是只會在第一次加載時運行一次,然后運行結果就被緩存了,以后再加載,就直接讀取緩存結果。要想讓模塊再次運行,必須清除緩存。
模塊加載的順序,按照其在代碼中出現的順序。
3.基本語法
暴露模塊:module.exports = value
或exports.xxx = value
引入模塊:require(xxx)
,如果是第三方模塊,xxx為模塊名;如果是自定義模塊,xxx為模塊文件路徑
require 命令用於加載模塊文件。require命令的基本功能是,讀入並執行一個JavaScript文件,然后返回該模塊的exports對象。如果沒有發現指定模塊,會報錯。
4.模塊加載機制
CommonJS模塊的加載機制是,輸入的是被輸出的值的拷貝。也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值
// lib.js var counter = 3; function incCounter() { counter++; } module.exports = { counter: counter, incCounter: incCounter, }; 上面代碼輸出內部變量counter和改寫這個變量的內部方法incCounter。 // main.js var counter = require('./lib').counter; var incCounter = require('./lib').incCounter; console.log(counter); // 3 incCounter(); console.log(counter); // 3 上面代碼說明,counter輸出以后,lib.js模塊內部的變化就影響不到counter了。這是因為counter是一個原始類型的值,會被緩存。除非寫成一個函數,才能得到內部變動后的值。
6.ES6模塊化
ES6 模塊的設計思想是盡量的靜態化,使得編譯時就能確定模塊的依賴關系,以及輸入和輸出的變量。
CommonJS 和 AMD 模塊,都只能在運行時確定這些東西。比如,CommonJS 模塊就是對象,輸入時必須查找對象屬性。
1.es6模塊化語法
export命令用於規定模塊的對外接口,import命令用於輸入其他模塊提供的功能。
/** 定義模塊 math.js **/ var basicNum = 0; var add = function (a, b) { return a + b; }; export { basicNum, add }; /** 引用模塊 **/ import { basicNum, add } from './math'; function test(ele) { ele.textContent = add(99 + basicNum); }
使用import命令的時候,用戶需要知道所要加載的變量名或函數名,否則無法加載。
為了給用戶提供方便,讓他們不用閱讀文檔就能加載模塊,就要用到export default命令,為模塊指定默認輸出。
// export-default.js export default function () { console.log('foo'); } // import-default.js import customName from './export-default'; customName(); // 'foo'
模塊默認輸出, 其他模塊加載該模塊時,import命令可以為該匿名函數指定任意名字。
2.ES6 模塊與 CommonJS 模塊的差異
1.CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。
// lib.js export let counter = 3; export function incCounter() { counter++; } // main.js import { counter, incCounter } from './lib'; console.log(counter); // 3 incCounter(); console.log(counter); // 4
2.CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口。
第二個差異是因為 CommonJS 加載的是一個對象(即module.exports屬性),該對象只有在腳本運行完才會生成。
而 ES6 模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。
ES6 模塊的運行機制與 CommonJS 不一樣。ES6 模塊是動態引用,不會緩存值,模塊里面的變量綁定其所在的模塊。
7.總結:
CommonJS規范主要用於服務端編程,加載模塊是同步的,這並不適合在瀏覽器環境,因為同步意味着阻塞加載,瀏覽器資源是異步加載的,因此有了AMD CMD解決方案。
AMD規范在瀏覽器環境中異步加載模塊,而且可以並行加載多個模塊。不過,AMD規范開發成本高,代碼的閱讀和書寫比較困難,模塊定義方式的語義不順暢。
CMD規范與AMD規范很相似,都用於瀏覽器編程,依賴就近,延遲執行,可以很容易在Node.js中運行。不過,依賴SPM 打包,模塊的加載邏輯偏重
ES6 在語言標准的層面上,實現了模塊功能,而且實現得相當簡單,完全可以取代 CommonJS 和 AMD 規范,成為瀏覽器和服務器通用的模塊解決方案。