Hi Da Gei Ho~ 我是 Winnie , 今天來到了第三天
前情提要:
在昨天的文章中,我們簡單的解釋了下 模組 與 模組化 是什麼之後,今天我們將繼續 簡單了解 JS模組化規範 的 演進過程。
不知道大家記不記得,在上篇的文章我們有說到 早期的JavaScript 是還沒有模組化的概念的,所以在當時 只能以function 檔案來區分並透過<script><script/>
的方式來載入,就像以下程式碼:
//Monday.js
function Monday(month, day){
return month+"/"+day
}
//Tuesday.js
function Tuesday(month, day){
return month+"/"+day
}
//Wednesday.js
function Wednesday(month, day){
return month+"/"+day
}
<html lang="en">
<body>
<script type="text/javascript" src="../lib/juqery.js"></script>
<script type="text/javascript" src="../js/Monday.js"></script>
<script type="text/javascript" src="../js/Tuesday.js"></script>
<script type="text/javascript" src="../js/Wednesday.js"></script>
</body>
</html>
**依照上面程式碼載入的方式,如果使用不小心很容易就會導致以下缺點:
//a.js
var Wednesday = '9/15'
.... 略
//b.js
var Wednesday = '9/16'
....略
console.log(Wednesday)
從上程式碼來看,原本應該 星期三應該是9/15,但因為其他命名相同的衝突而被覆蓋 輸出 9/16
引入的檔案相互依賴性高,順序很重要
如:Wednesday.js
依賴了Tuesday.js
那麼載入時就必須先使用Tuesday.js
再使用Wednesday.js
,一但順序相反,就很容易出錯
後來為解決 命名衝突及污染全域的問題 ,進而發展出 IIFE(立即執行函式) 模擬模組的方式,而IIFE的就如同他名字一樣,當下被宣告function就會立即執行。
其中在模擬模組化的過程中,還 使用到了 JavaScript的特性 閉包Closures 讓區域變數和方法鎖在內部,將需要給外部使用的變數傳出去,而這樣的好處就是區域性的變數 就不會汙染到全域性作用域。
(function(){
var name = 'winnie;
var sayHello = function (){
console.log('hello ! I'm'+ name);
}
sayHello(); // hello! I'm winnie
})()
雖然以上 實現了簡單的程式碼模組化,同時也解決了污染作用域的問題,但針對 管理 相互依賴的問題還是依然存在。
所以隨著技術的進步(老話一句),為了能讓人們更方便,所以後期在 Node.js 中大家也開始發展出許多方便的第三模組庫供人使用,因此針對模組化也有了不同的規範版本,其中 像是 我們常聽到的 CommonJS、 AMD 、 CMD 還有 近期的ES6 module
而在這麽多規範中,想必大家應該常常會看到 require/module.exports
與 import/exports
的用法吧?
是的!接著我們就要來介紹這兩個的規範,分別是 common.js & ES6 module (抱歉!其實 AMD、CMD也常見,後續有機會會再補上 )
如果大家有印象,在 Common JS標準最主要的規範就是輸出模組時用 module.exports
,引入時用 require
。
範例如下:
// banana.js
//一個一個 匯出
exports.banana = 'I m banana'
module.exports.banana = function(){}
//整體匯出
module.exports = { banana: 'I m banana', banana:function(){}}
const banana = require('./banana.js')
console.log(banana.banana) // 'I m banana'
而這個用法在當時也被Node.js採用 ,所以現今我們時常會在各種程式碼中 看到這個形式來做模組的輸出與引用。
但很可惜的是,雖然Common.js被Node.js採用成模組化的規範,但很可惜的是,在瀏覽器上面依然沒辦法使用 ,簡單來說 就是 瀏覽器 只看得懂 JavaScript 啊,而這也是我們 現今為何要使用 打包工具 的最主要原因。
對!終於提到 打包工具,現在你終於能稍微知道為什麼要用Webpack了啊!
畫重點!!就是在當時 瀏覽器的原生並不支援 CommonJS),必須透過打包工具的編譯 才能在瀏覽器上面使用。
而 ES6 出來之後,模組化 有了正式的規範,也就是近期很常看到的 import 與 export的形式
範例如下:
// hello.js
// 多個輸出名稱
export function sayHello(){
console.log('Hello');
}
export function sayBye(){
console.log('Bye');
}
// 單一輸出 (這邊需注意的是 一隻檔案中只能有一個 export default)
function sayGoodnight(){
console.log(‘Goodnight’)
}
export default sayGoodnight
---------------------------------
// other.js
// 引入hello模組的sayHello方法
import { sayHello, sayBye } from './hello';
sayHello(); // => Hello
sayBye(); // => Bye
這就是 ES6 Module 。
到這邊,你可能會想既然原生都支持了,為何還要打包工具?
但事情是這樣的,人生有十之八九不會照著我們想走的走。
所以在 ES6 Module 這邊也是一樣,雖然是瀏覽器原生支持的,但還是有在某些瀏覽器不支援的問題,就像下面這張圖一樣
而同時如果在 Node.js 上面試圖執行 import 這個語法,也還出現一個 SyntaxError: Unexpected identifier 的錯誤,因為 Node.js 不認識 import。但總不可能因為不支援就要拋棄他人寫好的給力模組吧。
綜合以上這些可能會碰到的問題,如果想要人生順利,我們還是需要透過 打包工具 來幫我們處理掉這些問題。
最後,這篇文章簡單的針對 JS中 模組化從有到無的出現過程及常見模組化規範(其實還少講了很多XD,像是AMD、CMD)進行了描述,雖然可能不是說得很詳細,但這篇文章的最主要目的還是想透過 模組化的微歷史 來了解 為何使用打包工具來編譯的目的。
如果文章有錯誤的地方,再麻煩不吝嗇的給予指教 謝謝 !!