本文重點將會以ES6的Module為主
文章將會提到以下幾點
Javascript當初設計的時候僅是網頁執行簡單腳本的語言,隨著時間的推演,一個網頁擁有大量複雜的腳本需要被執行,直到近年網頁所涵蓋的Javascript腳本越來越多,勢必要考慮將Javascript模組拆分,因此慢慢衍生一些模組化系統,在ES6之前比較知名的有CommonJS和AMD(Asynchronous Module Definition),ComonJS主要是設計給伺服器端的Node.js使用,AMD的目標則是給瀏覽器端,近年制定Javascript標準的ECMAScript在2015年將模組化的語法納入到標準當中,因此被稱為ES6 Moudle(ESM)。
在介紹ES moudle之前,首先得簡單講解一下模組是什麼,根據維基百科模組化設計
的介紹提到其旨在於將一個系統細分為許多小單元,稱為模組(module)或模塊(block),可以獨立的於不同的系統中被建立與使用,對應到現實生活中例如一台車子當中,具有煞車模組、引擎控制模組、傳動系統等等,以電腦為例擁有供電模組、散熱模組、記憶體模組,每個模組可能不僅只能使用在同一型號的電腦,藉由不同型號的裝置,共用模組可以達到大量製造、降低生產成本等等的優點。
回到Javascript的Module,其內容就是將一個功能或者類似的功能組合在一起的程式碼,而且其中也常被包含在更大的應用程式用來執行某些特定的任務。
大致上整理出以下幾點
例如我們在src的資料夾內有app.js
和data.js
和index.html如下
└─src
app.js
data.js
index.html
在index.html的script
使用type=module
的方式如下
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="module" src="app.js"></script>
</body>
</html>
在data.js
底下程式碼如下
export const data=[1,2,3,4,5,6,7,8];
在app.js
程式碼如下
import { data } from './data.js'
console.log(data);
這樣的做法就能如期印出data了,換言之就是在app程式當中引用了data的模組
需要注意的地方是當我們使用模組的時候,其程式碼部分將會自動成為嚴格模式
更多有關嚴格模式的說明可以參考MDN-Strict mode for modules
我們針對export分成下列兩種方式
named export 具名匯出
default export(預設匯出)
另外匯出的時候不一定需要取名字,因為在import的時候會取名,但是通常使用原本module的名字,換句話說引入的時候也可以改成你想要的名字
我們除了看到基本用法匯出資料以外,通常一個模組的檔案也會包含許多function,這邊示範如何匯出function
上面基本用法簡單介紹了一下匯出資料的方式,這裡使用default匯出function
這裡假設命名了一隻檔案叫做fn.js
檔案內容如下
export default function (name) {
console.log(name);
}
在另一支檔案app.js
裡面就是import直接使用,程式碼如下
import fn from "./fn.js";
fn("Danny");
上面示範如何預設匯出function 我們可能一隻檔案有很多function,因此可以使用具名匯出範例如下
export function sayHello() {
console.log("Hello");
}
export function sayMyName(name) {
console.log("Hi I am " + name);
}
在app.js使用大括弧的方式引入如下
import { sayHello, sayMyName } from "./fn.js";
sayHello();//Hello
sayMyName("Danny");//Hi I am Danny
這次原本的data.js
以下片段
export const array = [1, 2, 3, 4, 5, 6, 7, 8];
export function sayMyName(name) {
console.log(name);
}
export default function () {
console.log("你好");
}
在app.js改成以下片段
我們使用*
表示全部引入的意思,他會包含default和具名匯出的東西。
import * as myModule from "./data.js";
console.log(myModule);
這裡我們可以看到如下圖擁有一個key叫做default的屬性
這時候我們就可以解構賦值的方式將其提取出來使用
程式碼如下
import * as myModule from "./data.js";
console.log(myModule);
const { array, sayMyName } = myModule;
const { default: data } = myModule;
data();
console.log(array);
sayMyName("Danny");
因此可以知道的事情如下
備註:更多保留字的解釋可以參考MDN-保留字
另外如果想要export多個函式卻又不想要一直撰寫export
這個關鍵字的時候,可以使用包裹成物件的形式預設匯出
const foo = () => console.log('你好')
const foo2 = () => console.log('安安')
const foo3 = () => console.log('安安你好')
export default { foo, foo2, foo3 }
另一隻app.js檔案
import hiModule from "./data.js";
console.log(hiModule);
const { foo, foo2, foo3 } = hiModule; //解構賦值把函式取出
foo();
foo2();
foo3();
印出如下圖
我們在引入的時候也可以另外取名字的方式,通常會這麼做的時候是因為可能原先export的太過冗長或是在引用的檔案當中有相同的命名,因此會使用as
來做為額外命名
比較方便記憶的方式是as是英文alias的簡寫,而alias的中文有化名、別名的意思
我們的data.js裡面有以下內容
export const data=[1,2,3,4,5,6,7,8];
export function sayMyName(name){
return console.log(name);
}
引入的app.js,這時候可以看到下面的sayMyName被取名成say了。
import {data,sayMyName as say }from './data.js';
console.log(data);
say("Tom");
如果我們在原本的檔案當中不寫export關鍵字的話,一樣可以在其他要使用的地方引入,只是這樣的做法就被稱為side effect模組
原本的data.js
不寫入export
function hello(){
console.log("hello");
}
hello();
在app.js
引入
import("./data.js");
瀏覽器就會直接顯示hello,換句話說如果我們希望在引入的時候直接使用的話可以使用sideEffect的方式。
有時候我們會有多個模組,我們可以也可以透過一個檔案的方式將多個模組的檔案整合成一支檔案。
*
縮寫導入後導出先前提到*
具有全部的意思,因此我們可以使用*
,將其他模組的內容一次載入再導出整合成一支檔案。
在test.js
export const a = 1;
export const b = [2,3];
export default function(){
console.log("test");
}
需要注意的地方是 在data.js
,雖然export*
但其實並沒有導出test.js
的default。
export * from './test.js'; // 匯出此種方法並不會匯出在test的default
在app.js
撰寫如下
import * as module from "./data.js"
console.log(module);
const { a , b }= module;//使用解構賦值方式將其取出
如此一來印出module的時候會發現並沒有default的內容
為了能夠在重新導出的時候也包含了原先在test的defautl的內容,因此data.js
必須改成如下
export * from "./test.js"; // 匯出此種方法並不會匯出在test的default
//要匯出default的話要寫以下片段
export { default } from "./test.js";
在app.js
撰寫成以下片段,就能如期的使用剛剛在test所導出的function
import fn, * as module from "./data.js";
console.log(module);
fn();
const { a, b, test } = module; //使用解構賦值方式將其取出
另外我們一樣可以使用as的方式將預設模組導入之後再取名導出
再data.js的內容改成如下
export * from "./test.js"; // 匯出此種方法並不會匯出在test的default
//要匯出default的話要寫以下片段
export { default as test} from "./test.js";
在app.js的檔案使用大括號的方式就能夠如期使用,內容如下
import { a, b, test } from "./data.js";
console.log(a);
console.log(b);
test();
印出內容如下
在React中(或其他框架)我們可以透過module的方式做到component的檔案拆分。
例如以下就是將各個網頁上的component實際切分出的資料夾形式
├─API
├─component
│ ├─Aside
│ ├─Banner
│ ├─Card
│ ├─Footer
│ ├─Header
│ ├─Logo
│ ├─Pagination
│ ├─Sidebar
├─hook
├─pages
│ ├─Detail
│ ├─HomePage
│ ├─NotFound
│ └─Search
├─store
└─styles
在React中(或其他框架中),我們可以透過整合模組的技巧,創建一支index.js,簡化引入時候的程式碼。
export { default as Banner } from "./Banner/Banner";
export { default as Footer } from "./Footer/Footer";
export { default as Aside } from "./Aside/Aside";
export { default as Pagination } from "./Pagination/Pagination";
這樣在引入的地方就能減少程式碼的量,增加可讀性。
例如我們在page的js檔案,import的時候僅需要寫一行就好。
import { Aside, Header, Banner, Footer } from '../../component'
例如我們需要在撰寫邏輯程式碼的,地方減少程式碼量,增加可讀性,這個時候就可以將資料統整成一支檔案,舉例如下
export const JapaneseCharacter = [
{
hiragana: "あ",
katakana: "ア",
roma: "a",
},
{
hiragana: "い",
katakana: "イ",
roma: "i",
},
{
hiragana: "う",
katakana: "ウ",
roma: "u",
},
{
hiragana: "え",
katakana: "エ",
roma: "e",
},
{
hiragana: "お",
katakana: "オ",
roma: "o",
},
{
hiragana: "か",
katakana: "カ",
roma: "ka",
},
//日文字有五十個,以下省略
];
在需要日文資料的地方再import就好了。
import lodash from 'lodash'