當程式碼愈寫愈多時,除了最基本切割邏輯到不同檔案,也要有組織與管理程式碼的一套方式,「模組化」可能是最方便的一條道路,但它幫我們處理掉哪些問題呢?
今天就以 ES Module 為主來討論!
還記得在以前沒有前端框架的時代,寫的是單純的 .html
、.js
檔案,要把它們組合起來變成一個網頁,總是需要用到 <script>
tag,比如以前要使用 jQuery 總是需要這一行:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
那時沒想太多,只知道加了這一行之後,我就可以在任何 .js
裡面使用 $
這個變數,它變成一個全域變數,但有個前提,就是引入 .js
的順序要對:
// 正確
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<script src="scriptNeedJQuery.js"></script>
// 錯誤: '$' is not defined
<script src="scriptNeedJQuery.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
這裡就帶出了兩個主要的問題:
ES6 版本新加了 import
、export
兩個語法,引入了「模組化」的思維,讓 JavaScript 在開發大型複雜應用程式時,更為方便且易於管理。
只要在 <script>
加上 type="module"
的 attribute,就可以在 script 裡面使用 import
與 export
了:
// index.html
<script type="module" src="main.js"></script>
// main.js
import react from 'react';
...
基本的 export
分成兩種,加上 default
關鍵字可以不用特別命名(但只能有一個),否則需要命名:
// Default exports
export default 7;
// Named exports
export const monthDays = 31;
export const log = () => { console.log('Hello, world!'); };
透過上述兩種方式 export
,就可以用下列方式 import
,其中 default export 因為需要自行命名:
import weekDays, { monthDays, log } from './script.js'
console.log(weekDays); // 7
console.log(monthDays); // 31
log(); // Hello, world!
另外,官網也提到關於動態載入的作法,就是使用 import()
語法
注意容易混淆:
import A from B 是靜態載入
import(B) 是動態載入
有些 script 只有特定情況才需要時,可以使用動態載入,透過管理回傳的 Promise 物件,盡可能提升 performance,避免一次性載入太多用不到的 js,造成首次白畫面過長:
btn.addEventListener('click', () => {
import('./modules/test.js').then(result => {
console.log(result);
})
});
痾。。。我想不到...頂多算是相見恨晚?
畢竟在 ES module 之前,還有 CommonJS、AMD、UMD 等前輩,比較是一個各自實作的狀態,沒有統一的標準。
基本上 Module 已經是現在大型應用程式的標配了,甚至不用到多麼大型,隨著各種工具愈來愈先進,瀏覽器支援愈趨完整,要做到模組化已經是沒什麼成本的事情了。
懷念起以前在 index.html 裡面排列組合,引用了一大堆 CDN 的 script,處處擔心會不會有相依性的問題,還自己研究 IIFE 的寫法來避免變數汙染,不知道有模組化這樣方便的工具,花了許多冤枉時間,如果能專心拿去寫程式邏輯多好啊~