1 - 模塊化概述
傳統(tǒng)開發(fā)模式的主要問題:① 命名沖突,② 文件依賴
通過模塊化解決上述問題:
- 模塊化就是把單獨的一個功能封裝到一個模塊(文件)中,模塊之間相互隔離,但是可以通過特定的接口公開內(nèi)部成員,也可以依賴別的模塊
- 模塊化開發(fā)的好處:方便代碼的重用,從而提升開發(fā)效率,并且方便后期的維護
2 - 大一統(tǒng)的模塊化規(guī)范 - ES6模塊化
1. ES6模塊化
ES6模塊化出現(xiàn)之前,瀏覽器端和服務(wù)器端的模塊化是不同的,如下:
- 瀏覽器端的模塊化
① AMD(Asynchronous Module Definition,異步模塊定義)
代表產(chǎn)品為:Require.js
② CMD(Common Module Definition,通用模塊定義)
代表產(chǎn)品為:Sea.js
- 服務(wù)器端的模塊化
服務(wù)器端的模塊化規(guī)范是使用CommonJS規(guī)范:
① 使用require引入其他模塊或者包
② 使用exports或者module.exports導(dǎo)出模塊成員
③ 一個文件就是一個模塊,都擁有獨立的作用域
在ES6模塊化規(guī)范誕生之前,Javascript社區(qū)已經(jīng)嘗試并提出了 AMD、CMD、CommonJS 等模塊化規(guī)范,但是,這些社區(qū)提出的模塊化標準,還是存在一定的差異性與局限性,并不是瀏覽器與服務(wù)器通用的模塊化標準,例如:AMD 和 CMD 適用于瀏覽器端的 Javascript 模塊化,CommonJS 適用于服務(wù)器端的 Javascript 模塊化。
因此,ES6 語法規(guī)范中,在語言層面上定義了 ES6 模塊化規(guī)范,是瀏覽器端與服務(wù)器端通用的模塊化開發(fā)規(guī)范。
ES6模塊化規(guī)范中定義:
- 每個 js 文件都是一個獨立的模塊
- 導(dǎo)入模塊成員使用 import 關(guān)鍵字
- 暴露模塊成員使用 export 關(guān)鍵字
2. Node.js 中通過 babel 體驗 ES6 模塊化
Node.js中默認支持CommonJS模塊化規(guī)范,但是對于 ES6模塊化支持的并不是很好,所以通常需要借助babel這個第三方插件,才能在Node中體驗高級的ES6特性。
babel是一個語法轉(zhuǎn)換工具可以把高級的有兼容性的js代碼轉(zhuǎn)換成低級的沒有兼容性的js代碼。
步驟:
-
cd
到項目目錄,使用npm init -y
創(chuàng)建package.json文件 - 安裝babel的依賴包:
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node
,安裝完之后會創(chuàng)建node_modules文件夾和package-lock.json文件 - 安裝polyfill插件:
npm install --save @babel/polyfill
- 項目根目錄創(chuàng)建文件 babel.config.js文件,文件內(nèi)容如下
- 創(chuàng)建index.js文件,文件寫一行輸出代碼:
console.log("ok")
- 通過
npx babel-node index.js
執(zhí)行代碼(npx在高版本的npm中默認提供了,可以直接通過npx運行某些cli命令,這里就是通過npx運行babel-node命令,運行的文件是index.js) - 發(fā)現(xiàn)終端打印:ok,說明babel配置的沒問題
給index.js運行babel-node命令,babel運行之前會先讀取babel.config.js 文件內(nèi)容,再做代碼轉(zhuǎn)換。
// 語法轉(zhuǎn)換的數(shù)組,里面是語法轉(zhuǎn)換時可能用到的語法轉(zhuǎn)換插件
const presets = [
["@babel/env", {
targets: { // 轉(zhuǎn)換完畢的代碼最低要支持如下瀏覽器
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1"
}
}]
];
module.exports = { presets }; // 向外暴露出去,供babel使用
babel配置完成之后我們就可以在這個node項目下學習ES6模塊化的一些高級特性,比如ES6的模塊化導(dǎo)入。
3 - ES6 模塊化的基本語法
1. 默認導(dǎo)出 與 默認導(dǎo)入
創(chuàng)建m1.js文件,代碼如下:
// 定義私有成員 a 和 c
let a = 10
let c = 20
// 外界訪問不到變量d,因為它沒有被暴露出去
let d = 30
function show() {}
// 默認導(dǎo)出:將本模塊中的私有成員暴露出去,供其他模塊使用
export default {
a,
c,
show }
在index.js文件導(dǎo)入,代碼如下:
// 默認導(dǎo)入:導(dǎo)入模塊成員
import m1 from './m1.js'
console.log(m1) // { a: 10, c: 20, show: [Function: show] }
執(zhí)行:npx babel-node index.js
命令,打印輸出的結(jié)果為:{ a: 10, c: 20, show: [Function: show] }
- 每個模塊中,只允許使用唯一的一次
export default
,否則會報錯!如果在一個模塊中沒有向外暴露成員,其他模塊引入該模塊時將會得到一個空對象。 - 默認導(dǎo)出和默認導(dǎo)入都是對象,不能是其他東西,默認導(dǎo)出和默認導(dǎo)入最常用。
2. 按需導(dǎo)出 與 按需導(dǎo)入
m1.js文件代碼如下:
// 向外按需導(dǎo)出變量 s1
export let s1 = 'aaa'
// 向外按需導(dǎo)出變量 s2
export let s2 = 'ccc'
// 向外按需導(dǎo)出方法 say
export function say = function() {}
在index.js中按需導(dǎo)入:
// 導(dǎo)入模塊成員
import { s1, s2 as ss2, say } from './m1.js' // 把s2起了個別名ss2
console.log(s1) // 打印輸出 aaa
console.log(ss2) // 打印輸出 ccc
console.log(say) // 打印輸出 [Function: say]
執(zhí)行:npx babel-node index.js
命令,打印輸出的結(jié)果如上。
注意:
- 每個模塊中,可以多次使用按需導(dǎo)出,只能使用一次默認導(dǎo)出。
- 默認導(dǎo)出/導(dǎo)入是對象,按需導(dǎo)出/導(dǎo)入是成員。
- 默認導(dǎo)出和按需導(dǎo)出可以同時使用,如果同時使用,則需要按如下格式導(dǎo)入,其中m1中是默認導(dǎo)出的成員。
import m1, { s1, s2 as ss2, say } from './m1.js'
3. 直接導(dǎo)入并執(zhí)行模塊代碼
有時候,我們只想單純執(zhí)行某個模塊中的代碼,并不需要得到模塊中向外暴露的成員,此時,可以直接導(dǎo)入并執(zhí)行模塊代碼。
m2.js文件代碼如下:
// 在當前模塊中執(zhí)行一個 for 循環(huán)操作
for(let i = 0; i < 3; i++) {
console.log(i)
}
在index.js中導(dǎo)入:
// 直接導(dǎo)入并執(zhí)行模塊代碼
import './m2.js'
終端執(zhí)行如下命令:
npx babel-node ./index.js
// 打印:0 1 2