iT邦幫忙

2021 iThome 鐵人賽

DAY 27
0
自我挑戰組

JavaScript 奇奇怪怪的核心觀念系列 第 27

(Day27) ESM 模組化拆檔

前言

隨者前端需求越來越多,前端工程師在管理程式碼上的需求也越來越重,幸好 ES6 引入時 JavaScript 有引入的 ESM 的拆檔功能,以方便開發者管理程式碼,而目前大多數的 JavaScript 套件也有都使用到 ESM 的方法來做開發以及管理,算是前端工程師必學的方法之一了,而 ESM 他的全名是「ES6 Modules or JavaScript Modules」。

ESM 基本介紹

如果要使用 ESM 的模組化,首先必需在 <script> 標籤添加 type="module" 這樣 JavaScript 變能使用模組化的功能,而這個就模組功能,簡單來說就是使用 export 來做匯出動作 、 import 來做匯入動作,而匯出和匯入大致可分成:

  • 預設匯出、預設匯入
  • 具名匯出、具名匯入

由於 預設匯出 一定會搭配 預設匯入 ,具名匯出 一定會搭配 具名匯入 因此接下來就來講講這兩種狀況:

具名匯出、具名匯入

具名匯出

故名思義具名匯出,就會是以有名稱的方式將資料匯出,具名匯出在同一個 JS 檔案上,並沒有限制,可以同一個 JavaScript 檔案匯出多此,具名匯出則大致可分為以下幾種:

  1. export 後使用 letconst 宣告:
export let str = 'test'
export const array = [1,2,3]
  1. export 後使用函式陳述式:
export function fn() {
  console.log('具名匯出')
}
  1. 將變數資料整合起來使用以物件縮寫方式,統一匯出:
let str = 'test'
const array = [1,2,3]
function fn() {
  console.log('具名匯出')
}
export const obj = {
str, array, fn
}

具名匯入

由於具名匯出以經帶有名稱了,因此當我們使用 具名匯入 時,匯入的名稱,會是 和具名匯出 時相同的名稱來做匯入動作,而具名匯入一般狀況會是 import { ... } form './app.js' 的方式匯入。

/*  具名匯出檔案 app.js */
let str = 'test'
const array = [1,2,3]
function fn() {
  console.log('具名匯出')
}
export const obj = {
str, array, fn
}
export const number = 123

// 具名匯入檔案
<script type="module">
    import { obj , number } from './app.js' 
    console.log(obj, number)  // obj: { str: "test", array:[1,2,3] , fn: fn() } , 123
 </script>

而具名匯入後其實可以更改名稱的,會使用 as 來將具名匯入的檔案更改名稱:

<script type="module">
    import { obj ,  number as num } from './app.js' // 將 number 改為 num
    console.log(obj, num )  // obj: { str: "test", array:[1,2,3] , fn: fn() } , 123
 </script>

同時具名匯入也可以一口氣匯入所有資料,會使用 * 字號時,搭配 as 將匯入的資料,賦予到一個新的變數叫做 app 上,不過實做中這種一口氣匯入寫法通常較少用到:

<script type="module">
    import  * as app  from './app.js'
    console.log(app.obj, app.number )  // obj: { str: "test", array:[1,2,3] , fn: fn() } , 123
 </script>

預設匯出、預設匯入

預設匯出

當匯出部分使用 export default 就可以知道這組是使用 預設匯出 與 預設匯入,預設匯出同樣可以匯出任何資料,字串、陣列、函式,最常見的就會是匯出一個物件,但是無法使用 變數/常數 的形式來匯出,而每個一個 JS 檔,只能使用一次預設匯出。

/* app.js */

export default {
  str : 'test',
  array : [1,2,3],
  fn() {
  console.log('預設匯出')
  }
}

預設匯入

由於預設匯出時,並沒有使用任何名稱,因此匯入時並需使用一個名稱來接收資料

import app from './app.js'
console.log(app.str) // test
console.log(app.array) // [1,2,3]
app.fn() // 預設匯出

同時匯入預設、具名

匯出時可同時使用預設匯出、具名匯出,而匯入部分要同時使用兩種匯入方法也是 OK 的,不過要使用這種方法,使用上會有個固定的格式。
需要同時使用兩種引入方式時,import 會先寫入預設匯入部分,設定好後,自定義名稱後方會用上逗號將 預設匯入 、具名匯入的名稱分開,而逗號後方會是 * as xxx 來將具名匯入一口氣匯入進來。

/*  匯出檔案 app.js */
let str = 'test'
const array = [1,2,3]
function fn() {
  console.log('具名匯出')
}
export const obj = {
str, array, fn
}
export default   123

// 匯入檔案
<script type="module">
    import number , * as obj  from './app.js' // number 是預設匯入, obj 則是具名匯入。 
    console.log(obj, number)  // obj: { str: "test", array:[1,2,3] , fn: fn() } , 123
 </script>

使用 type= module 後的變化

而最後也在補充一下,當我們在 <script> 標籤添加 type= module 後其實 JavaScript 會發生一些變化,如下:

  • 各個 Script 作用域獨立:
    在原本 JavaScript 中是可以跨 <script> 標籤來做資料存取動作,比如:
<script>
  const name = 'Ryder';
 </script>
  
<script>
  console.log(name); // Ryder
</script>

如果在 <script> 標籤添加 type= module<script> 標籤的作用域都會被獨立,是無法在取得另一個 <script> 標籤的資料,比如:

<script>
  const name = 'Ryder';
 </script>
  
<script>
  console.log(name); // ReferenceError: name is not defined
</script>
  • 開啟嚴格模式

在 this 章節有提到 簡易呼叫 的 this 會指向 window,但當我們在 <script> 標籤添加 type= module 後,因為嚴格模式開啟 簡易呼叫的 this 會從 window 變為 undefined

使用前:

<script>
function fn() {
  console.log(this) // window
}
fn() 
</script>

使用後:

<script type="module">
function fn() {
  console.log(this) // undefined
}
fn() 
</script>

參考文獻


上一篇
(Day26) 使用 fetch 串接 Ajax
下一篇
(Day28) ES6 展開運算子與其餘參數
系列文
JavaScript 奇奇怪怪的核心觀念30

尚未有邦友留言

立即登入留言