模塊化開發(fā)
模塊化只是一種思想
模塊化演變過程
- Stage 1 - 文件劃分方式
- 將功能與數(shù)據(jù)放置到不同的文件當中
- 約定每個文件都是一個獨立的模塊
- 缺點:
- 每個模塊都在全局作用域下工作,污染全局作用域,容易出現(xiàn)命名沖突
- 無法管理模塊之間的依賴關(guān)系
- 模塊內(nèi)的成員可以被其他模塊隨意修改
- 完全依靠約定,當項目變得龐大以后,難以維護
- Stage 2 - 命名空間方式
- 每個模塊只暴露一個全局對象
- 所有成員與方法都掛載在這個全局對象下
- 解決了全局作用域污染與命名沖突的問題
- 缺點:
- 依然無法管理模塊之間的依賴關(guān)系
- 模塊的內(nèi)的成員依然可以被修改
- Stage 3 - IIFE(立即執(zhí)行函數(shù))方式
- 使用立即執(zhí)行函數(shù),為模塊創(chuàng)建私有空間
- 對于需要對外暴露的內(nèi)容,可以掛載到window對象上
- 解決了模塊內(nèi)成員可以被隨意修改的問題
- Stage 4 - 模塊化規(guī)范出現(xiàn)
- CommonJS規(guī)范 - NodeJS模塊化規(guī)范
- 一個文件就是一個模塊
- 每個模塊有單獨的作用域
- 通過module.exports導出成員
- 通過require函數(shù)加載模塊
- 同步模式加載模塊(在瀏覽器環(huán)境效率不高)
- AMD(Asynchronous Module Definition)- 異步模塊定義規(guī)范
- require.js實現(xiàn)了AMD規(guī)范,同時是一個功能強大的模塊加載器
- 約定使用define函數(shù)定義一個模塊,接收三個參數(shù)
- 第一個參數(shù):模塊名稱
- 第二個參數(shù):聲明依賴數(shù)組
- 第三個參數(shù):提供私有空間的函數(shù),通過return向外部導出成員
- 使用require函數(shù)加載模塊
- 絕大多數(shù)第三方庫都支持AMD規(guī)范
- AMD模塊使用起來相對復雜
- 模塊JS請求相對頻繁
- CMD(Common Module Definition)- 通用模塊定義規(guī)范
- 由淘寶前端團隊推出的sea.js定義并實現(xiàn)了CMD規(guī)范
- 類似CommonJS規(guī)范
- 通過require加載依賴
- 通過module.exports或exports對外暴露成員
- 被require.js兼容
- CommonJS規(guī)范 - NodeJS模塊化規(guī)范
模塊化標準規(guī)范
ES Module - ES2015中定義,并在語言層面實現(xiàn)的模塊化規(guī)范
模塊化的最佳實踐,NodeJS環(huán)境下使用CommonJS規(guī)范,瀏覽器環(huán)境下使用ES Module
ES Module特性
- 自動采用嚴格模式
- 運行在獨立私有作用域中
- 通過CORS方式請求外部JS模塊
- 即script被標識為type=module方式引入外部資源時,會受到瀏覽器同源策略限制
- script加載外部JS時,會延遲執(zhí)行腳本,等同于使用defer屬性,不影響頁面渲染,執(zhí)行順序不按引用順序
ES Module的導入導出
-
通過export導出
-
常見用法
export const a = 1 export const fn = () => {} export default 123 export { a, fn as b } export t from './module.js' -
注意事項
-
export { obj }與export default { obj }-
export { obj }export后面跟的不是字面量對象,而是固定的語法 -
export default { obj }export default后面跟的是一個值,這里{ obj }表示字面量對象
-
- export導出的是成員的引用,而不是成員值的拷貝,這與CommonJS不同
-
-
-
通過import導入
-
常見用法
import { a, b, default as n } from './es-module.js' -
注意事項
-
import { obj } from xxximport后面的括號不是解構(gòu)寫法,而是固定的語法 - import導入的對象是只讀的,不允許修改
- import導入文件路徑不可省略(相對路徑或者絕對路徑)
- 不支持表達式方式,如果需要動態(tài)加載模塊,可以使用import()函數(shù),該函數(shù)返回一個Promise對象
-
-
ES Module瀏覽器環(huán)境polyfill
browser-es-module-loader
- 工作原理:
- 讀取ES Module腳本
- 交給babel進行轉(zhuǎn)換
- 執(zhí)行轉(zhuǎn)換后的代碼
- 注意事項
- 在支持ES Module的瀏覽器環(huán)境下,會導致重復執(zhí)行
- 可以使用script的nomodule屬性,只會在不支持ES Module的瀏覽器環(huán)境下加載腳本
ES Module in Node.js
Node.js中目前可以使用ES Module語法加載或者導出模塊,屬于實驗特性
-
要在node環(huán)境下使用ES Module
JS文件需要使用.mjs結(jié)尾(新版本node在package.json中標識type: module時,可以直接使用.js文件后綴,此時想使用CommonJS模塊,需使用.cjs結(jié)尾)
-
執(zhí)行時,需要添加
--experimental-modules參數(shù)開啟實驗特性// 注意,必須先跟--experimental-modules參數(shù),再接文件名 node --experimental-modules import.mjs
-
使用import加載其他模塊
- 第三方模塊:無法使用提取成員的方式
- 系統(tǒng)內(nèi)置模塊:官方做了兼容性處理,可以正常提取成員
- CommonJS模塊:導出一個對象,因此無法使用提取成員的方式加載模塊,可以使用加載default默認對象的方式
CommonJS模塊無法直接加載ES Module
-
ES Module與CommonJS模塊的差異
- ES Module下無法訪問CommonJS內(nèi)置成員及方法
require、module、exports、__filename、__dirname
- ES Module下無法訪問CommonJS內(nèi)置成員及方法