1-7 模組的匯出與匯入

模組的概念#

模組最重要的功能就是讓開發者能夠將程式碼進行分類整理和重複使用,具體來說就是把程式碼根據某些原則進行拆檔的動作。一個完整的應用程式中勢必會包含許多不同的功能,但不同的功能之間卻可能有些操作邏輯是相同的。

為了開發上更容易維護,我們不會把所有功能的程式碼都寫在同一支檔案中,這會使得這支檔案非常龐雜,未來需要修改或調整時,沒辦法很有效率地去找到要修改的地方,甚至有可能改錯地方。因此在模組系統中,我們可以把程式碼片段進行拆檔的動作,像是把首頁畫面的程式碼拆成一隻檔案、登入頁面的程式碼再拆成一隻檔案,而不是把它全部放在同一支檔案中。透過模組系統,開發者便可以根據需要,依據不同的原則將程式碼進行拆檔與分類的動作。

接著,在不同頁面中也可能會需要使用到相同的功能,例如,網頁中的許多頁面可能都需要使用到「取得使用者會員資料」的這個功能,雖然可以在每個頁面中都撰寫獨立但相同的程式碼去取得使用者的會員資料,但這樣未來若有需要調整時,會發現只是改一個會員資料的功能卻需要動到每一支檔案,顯然比較好的做法是把這個會被共用到的方法獨立拆分成一個檔案,讓這個方法可以在各頁面中透過匯入的方式重複被使用,未來若有需要調整,只需要修改這一支檔案,所有頁面的功能就會連帶更新,而不需要把每支檔案都打開來一一修改。透過模組系統,開發者便可以根據需要,適時的讓程式碼被重複使用而無需重複撰寫。

JavaScript 中不同的模組系統#

由於歷史及執行環境實作上不同的緣故,在 JavaScript 中目前主流使用了兩套不同的模組系統,其中一套是在後端伺服器(Node.js)上使用的模組系統,稱作 CommonJS;另一套則是在前端瀏覽器(Browser)上使用的模組系統稱作 ES modules。

note

💡 Tip:一般能夠運行在電腦主機上的 JavaScript 稱作 Node.js。原本 JavaScript 是只能在瀏覽器環境上運行,沒辦法離開瀏覽器獨立運作。後來透過 Google Chrome 提供的 V8 引擎,開發者只需要在電腦上安裝 Node.js 後就可以讓 JavaScript 獨立運行於電腦上,自此 JavaScript 從瀏覽器中解放出來,並提供許多與伺服器有關的功能可以使用,開始可以做為後端伺服器程式語言的選擇之一。

因為 React 是屬於前端框架,也就是會在瀏覽器上執行的,所以使用的模組系統即是 ES modules。這裡我們將針對 ES modules 的使用來進一步說明。ES modules 的全名是 ECMAScript modules,沒錯,與之前我們講 ES6 時談到的 ECMAScript 是相同的,而這套模組系統所使用的語法也同樣被定義在 ES6 中。

note

💡 Tip:為了解決 JavaScript 開發者常需要在兩套不同模組系統中切換語法,Node.js 目前也支援 ES module 的模組語法可以使用,但本書撰寫時此功能仍屬於實驗性質。

在 ES module 中要把程式碼片段匯出只需要使用 export 這個關鍵字,在另一隻檔案匯入時則使用 import 這個關鍵字。

實名匯出(Named Export)#

直接定義變數並匯出#

舉例來說,透過 export 可以直接定義變數並匯出:

// 在 utils.js 這支檔案直接定義變數並匯出
export const deviceName = 'iPhone';
export const price = 24900;
export const logPrice = (price) => {
console.log('price: ', price);
};
export function logDeviceName(deviceName) {
console.log(deviceName);
}

在其他檔案匯入時,只需要使用 import { 變數名稱 } from '<檔案路徑>' 即可使用匯出的變數,例如:

import { deviceName, price, logPrice, logDeviceName } from './utils';
console.log(deviceName, price);
logPrice(1000); // price: 1000
caution

匯入的變數名稱需要和匯出時相對應。

先定義好變數再匯出#

另外我們也可以先把變數都定義好,在檔案最後的地方把要匯出的變數執行 export{},例如:

const deviceName = 'iPhone';
const mobilesOnSale = ['Samsung', 'Apple', 'Huawei'];
const offers = {
priceCurrency: 'TWD',
price: '26,900',
};
const logPrice = (price) => {
console.log('price: ', price);
};
function logDeviceName(deviceName) {
console.log(deviceName);
}
export {
deviceName,
mobilesOnSale,
offers as productDetail, // 匯出時可以透過 as 進行名稱的修改
logPrice,
logDeviceName,
};

這裡我們除了匯出之外,還透過 as 將原本匯出的變數 offers 改名為 productDetail

caution

要特別留意這裡 export 後的大括號並不是物件,而是匯出用的語法,千萬不要在裡面使用 key-value 這樣的寫法!

匯入時同樣只需要根據匯出時的變數對應使用 import{} 即可,另外在 import 時一樣可以透過 as 修改匯入後到檔案中的變數名稱:

import {
deviceName as device, // 也可以透過 `as` 修改匯入後到檔案中的變數名稱
productDetail, // 要用匯出時的名稱
} from './utils';
console.log(device, productDetail);

預設匯出(default export)#

最後一種很常見的匯出方式稱作預設匯出(default export),從上面實名匯出的例子中可以看到,匯入時的變數名稱需要和匯出時相對應,隨便取名的話會無法取得對應的變數,因此開發者一定要很清楚匯出的變數有哪些才能進行匯入。但如果我們希望讓開發者可以直接匯入,不用先去管匯出的變數有哪些的話,則可以使用預設匯出的方式。

預設匯出的語法是在 export 後加上 defaultdefault 後則直接帶入你想要匯出的東西即可,例如:

// utils.js
export default ['Samsung', 'Apple', 'Huawei'];

匯入的時候可以自己隨意取名,這裡我們取做 brands,就可以直接使用:

import brands from './utils';
console.log(brands);

要特別留意的是,和實名匯出不同,如果你在 export default 後接的是 {} ,這個 {} 表示的就是物件,裡面放的就會是物件的屬性名稱和屬性值,因此不能再用 as 去修改名稱,例如:

// utils.js
const deviceName = 'iPhone';
const mobilesOnSale = ['Samsung', 'Apple', 'Huawei'];
const logPrice = (price) => {
console.log('price: ', price);
};
// export default 後直接帶入要匯出的東西
// 這裡是會直接匯出「物件」,因此不能在裡面使用 "as" 語法
export default {
deviceName, // deviceName: deviceName 的縮寫
mobilesOnSale, // mobilesOnSale: mobilesOnSale 的縮寫
logPrice, // logPrice: logPrice 的縮寫
};

匯入時也會是一個物件,若要使用裡面的資料,需要使用物件的方式來操作:

// myPhone 會是物件
import myPhone from './utils';
// 透過物件的方式操作
console.log('Device:', myPhone.deviceName);
// 也可以透過解構賦值將需要的屬性取出
const { logPrice } = myPhone;
logPrice(24900);
caution

一隻檔案最多只會有一個 export default,另外雖然實名匯出和預設匯出可以在一隻檔案中同時使用,但一般不太建議這麼做。