﻿# 網站如何開始使用 ES6 / ES2015 模組化技術進行前端開發

在 2019 的今天，還有多少人在使用**傳統前端技術**在開發 Web 應用程式？撇除市面上知名的 SPA 框架不說，就算你公司內部還在使用 jQuery 開發網頁，你依然可以將自己升級到使用**現代化前端技術**的前端工程師。今天這篇文章，將分享如何將現有傳統網站逐步升級，改用如 ES6 / ES2015 等語法，以及透過好用的模組打包工具(module bundler)將全新的 JS 語法轉譯為舊版瀏覽器也能執行的 JS 語法。

### 開始使用 ES6 Modules 組織程式碼

知名的練習專案 [TodoMVC](http://todomvc.com/) 有提供一個 [Vanilla ES6](https://github.com/tastejs/todomvc/tree/master/examples/javascript-es6) 版本，我擷取這個專案的 HTML、CSS 與 JavaScript 部分，然後將所有工具移除，弄了一個相當純粹的 [Vanilla ES6](https://github.com/doggy8088/todomvc-vanilla-es6-modules) 版本。

> [https://github.com/doggy8088/todomvc-vanilla-es6-modules](https://github.com/doggy8088/todomvc-vanilla-es6-modules)

由於從 Chrome 61 開始 (2017/9/5 Released) 就從瀏覽器原生支援 ES Module (ESM) 模組化技術，所以你完全可以在不使用 module bundler 工具的輔助下，直接使用 ESM 進行開發，而且可以使用大部分 ES6 語法。

從我這份 [TodoMVC Vanilla ES6](https://github.com/doggy8088/todomvc-vanilla-es6-modules) 範例的 [index.html](https://github.com/doggy8088/todomvc-vanilla-es6-modules/blob/master/index.html#L42) 第 42 行可以看見，你只要在 `<script>` 標籤中加入 `type="module"` 屬性 (Attribute)，就可以啟用 ESM 機制。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/4d7102553f91a25c1b66-image_3179.png)

一般來說，當你使用了 ESM 模組化技術，我們會將所有 JavaScript 檔案集中一個進入點，其他需要的模組，只要透過 [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) 語法就可以匯入使用，如此一來便可依所需載入必要的程式碼，也能限制 JS 全域變數的使用。

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/4f1d28be1ecf9deb01aa-image_3180.png)

我重新整理過後的這個範例程式，如果要能順利在瀏覽器中執行，可以參考以下步驟，透過 [lite-server](https://www.npmjs.com/package/lite-server) 這個輕量的開發伺服器，就可以快速啟動網站進行測試：

1.  透過 npm 安裝 [lite-server](https://www.npmjs.com/package/lite-server)
    
    ```bash
    npm install -g lite-server
    ```
    
2.  啟動本地開發伺服器
    
    ```bash
    lite-server
    ```
    
3.  此時瀏覽器會自動開啟 `http://localhost:3000/` 網頁
    

只要你可以開始使用 ESM 模組化技術，你的 JavaScript 視野就此打開，正式進入現代化的前端世界！

### 使用 Parcel 將新式語法轉為舊版瀏覽器相容語法

市面上知名的 module bundler 非常多，比較知名的有 [webpack module bundler](http://webpack.github.io/)、[rollup.js](https://rollupjs.org/)、[Parcel](https://parceljs.org/)、[Google Closure Compiler](https://developers.google.com/closure/compiler/) 等等。其中最熱門的應該是非 [webpack](http://webpack.github.io/) 莫屬，這套同時也是 [Angular](https://angular.io/) 目前預設的模組打包工具。

這篇文章我將推薦使用 [Parcel](https://parceljs.org/) 當做新手入門的模組打包工具，因為這套 module bundler 的最主要特色，就是「完全零設定」，一個設定檔都不用寫，全自動幫你的網頁所用到的 JavaScript 或 CSS 進行自動封裝。相較於 [webpack](http://webpack.github.io/) 來說，這套 Parcel 可說是既親民又強大，而且 bundle (打包) 的速度一點也不慢！

以下就是在目前的專案使用 Parcel 進行封裝的步驟說明：

1.  安裝 [parcel-bundler](https://www.npmjs.com/package/parcel-bundler) 套件
    
    ```bash
    npm install -g parcel-bundler
    ```
    
    ```bash
    yarn global add parcel-bundler
    ```
    
2.  執行 Parcel 並啟動 Parcel 開發伺服器
    
    ```bash
    parcel index.html --open
    ```
    
    > 使用 `--open` 參數會自動開啟預設瀏覽器進入測試網站。
    

此時你就可以連到 `http://localhost:1234` 測試網站功能，且所有從 `src/app.js` 匯入進來的檔案，以及所有相依的檔案，都會自動透過 Parcel 自動封裝 (bundle)，最後濃縮成一支 `*.js` 檔案。不僅如此，他還會自動幫你修正 `index.html` 檔案的內容，將載入的 JS 與其相依的模組，全部自動綑綁在一起。除了 JS 支援，頁面中的 CSS 也會被視為可以自動打包的資產(Assets)，也會全自動 bundle 完成。

> **請注意**：使用 module bundler 之後，原本一個頁面中如果載入多支 JS 檔或 CSS 檔，通常都會修改成只載入一支「主要 JS 檔案」，其他相依的檔案就會透過 ES6/ES2015 的 `import` 匯入進來。

由於 Parcel 預設採用 [Babel](https://babeljs.io/) 對專案內的 JavaScript 檔案進行轉譯，而目前最新版的 Babel 7 在轉譯 JS 的時候，預設是採用 ES2015 以上版本。也就是說，當你在專案中用到了 ES2016 或 ES2017 的 JS 語法，都會全自動轉換為 ES2015/ES6 的語法。

如果你走到這一步都非常順利，那我在此恭喜你，您已經成為一位使用**現代化前端技術**的**現代化前端攻城獅**了！

### 使用 Parcel 產生連 IE9 也能運行的網站

Internet Explorer 已經漸漸成為時代的眼淚，不過在企業尚未全面升級至現代化瀏覽器之前，總還是需要面對 IE 的荼毒。

目前 Babel 預設採用 [@babel/preset-env](https://babeljs.io/docs/en/babel-preset-env)，基本上你不用做任何設定，就會自動判斷市佔率高於 `0.25%` 的各個瀏覽器版本。

但如果你真的想讓 IE9+ 也能支援 ES2015 各種語言特性的話，還是必須對這個範例專案做出些許調整，步驟如下：

1.  修正 `index.html` 將 `type="module"` 移除
    
    ```html
    <script src="src/app.js"></script>
    ```
    
2.  加入 npm 設定檔：`package.json`
    
    你可以透過 `npm init -y` 快速建立預設 `package.json` 檔案。
    
    ```json
    {
      "name": "todomvc-vanilla-es6-modules",
      "version": "1.0.0",
      "dependencies": {},
      "devDependencies": {},
      "scripts": {}
    }
    ```
    
3.  安裝 [js-polyfills](https://www.npmjs.com/package/js-polyfills) 套件 (此套件就是讓 IE9 支援 ES2015 APIs 的利器)
    
    ```bash
    npm install -D js-polyfills
    ```
    
    > 雖然 Babel 內建支援 [@babel/polyfill](https://babeljs.io/docs/en/babel-polyfill) 套件，但是對 IE 的支援度完全不足，我想應該是 Babel 社群沒多少人想認真支援 IE 吧！ XD
    
4.  修正 `src/app.js` 檔案，匯入 `js-polyfills` 套件
    
    ```javascript
    import 'js-polyfills';
    ```
    
5.  執行 Parcel 並啟動開發伺服器
    
    ```bash
    parcel index.html
    ```
    
    ※ 各位有沒有發現，我們在使用 Parcel 的過程中，完全不用設定這套工具！所有的 bundle 行為都已經預設內建，直接執行就能用！
    

完成上述步驟之後，就可以讓 IE9+ 安心的瀏覽網站，所有 ES6/ES2015 全新的 API 也可以順利的在網站中使用。

> 如果你有用 Git 版控的話，建議加入 `.gitignore` 版控忽略檔，避免不必要的檔案被加入到版控之中：
> 
> ```bash
> # Dependency directories
> node_modules/
> 
> # Yarn Integrity file
> .yarn-integrity
> 
> # parcel-bundler cache (https://parceljs.org/)
> .cache
> 
> # distribution files
> dist/
> 
> # package-lock.json file
> package-lock.json
> ```

### 使用 Parcel 自動編譯 TypeScript 原始碼的網站

JavaScript 是個**弱型別**的程式語言，你無法在**開發時期**判斷型別，所有型別的問題都只能在**執行時期**才有機會被發現，你必須執行到那一行有問題的程式碼，才會知道是否發生錯誤。這個狀況會導致當你程式碼變多時，有許多問題是很難第一時間發現的。

此時 [TypeScript](http://typescriptlang.org/) 的出現，無疑是一種救贖。他讓你在開發時期，透過 TypeScript 編譯器，就能提早得知 JavaScript 程式碼可能出現的問題，並在第一時間修正他。不過，學習 TypeScript 是有點門檻的，你過往寫 JS 那種自由奔放的靈魂，到了 TypeScript 的世界，可能就會覺得礙手礙腳。你必須靜下心來，認真研讀 TypeScript 如何宣告型別，並且認真面對加入型別後的種種限制。

我將用以下步驟，示範如何將目前這個專案升級到使用 TypeScript 進行開發：

1.  將所有 `*.js` 檔案批次更名為 `*.ts` 檔案
    
    ```batch
    cd src
    rename *.js *.ts
    ```
    
2.  將所有 `*.js` 檔案中 `import` 語法的 `.js` 部分移除
    
    例如以下 `import` 語法：
    
    ```bash
    import Controller from './controller.js';
    import {$on} from './helpers.js';
    import Template from './template.js';
    import Store from './store.js';
    import View from './view.js';
    ```
    
    請全部改成如下：
    
    ```bash
    import Controller from './controller';
    import {$on} from './helpers';
    import Template from './template';
    import Store from './store';
    import View from './view';
    ```
    
    > 如果你用 VSCode 的話，可以用全部取代的方式，搭配正規表示式就可以一次取代完成：
    > 
    > -   搜尋：`import (.*).js';`
    > -   取代：`import $1';`
    
3.  修正 TypeScript 型別問題
    
    請參考[此版本](https://github.com/doggy8088/todomvc-vanilla-es6-modules/commit/72286a378b559532699819c314d7b19c3f662091?diff=split)的變更紀錄。
    
4.  修正 `index.html` 將 `src/app.js` 修改為 `src/app.ts`
    
    ```html
    <script src="src/app.js"></script>
    ```
    
    改為
    
    ```html
    <script src="src/app.ts"></script>
    ```
    
5.  執行 Parcel 並啟動開發伺服器
    
    ```bash
    parcel index.html
    ```
    

各位有沒有再次發現，我們在使用 Parcel 的過程中，即便原始碼全部變成 TypeScript 也完全不用進行設定，連需要的 typescript 套件也會自動安裝，完全是佛心來著！

### 結語

本篇文章以一個 ES6 專案開始，讓大家了解原來 Google Chrome 瀏覽器已經原生支援 ESM 模組化技術，在不用 module bundler 的幫助下，你已經可以開始進行開發與測試。當寫到一定程度後，搭配著現成好用的 module bundler 工具，即可讓舊版瀏覽器也能順利瀏覽網站，你幾乎沒有理由不用現代化的前端工具，來優化目前程式的品質。

當然，要寫好 JS 沒那麼簡單，有些真正基礎又重要的觀念，必須紮紮實實的建立，我在 [我要成為前端工程師！給 JavaScript 新手的建議與學習資源整理](/post/2016/02/02/JavaScript-novice-advice-and-learning-resources) 這篇文章中已經明確的點出，大部分的 JavaScript 開發者都是靠自己腦補出來寫 code 的，要培養出好的、正確的觀念，確實相當不容易。

我這幾年所開設的 [JavaScript 開發實戰：核心概念篇](https://coolrare.accupass.com/) 課程，目前已經影響上千名學員打好 JavaScript 的堅實基礎，你很難想像花這一天的時間，可以對你未來的 JavaScript 開發生涯帶來多大幫助，如果你還沒有親身體驗過這堂課程，強烈建議你真的可以來上課，感受不一樣的學習體驗。

### 相關連結

-   [我要成為前端工程師！給 JavaScript 新手的建議與學習資源整理](/post/2016/02/02/JavaScript-novice-advice-and-learning-resources.aspx)
-   [前端工程師必須學會的現代化前端開發工具](/post/2015/08/13/essential-frontend-tools-2015.aspx)
-   [Parcel](https://parceljs.org/)
-   [Babel · The compiler for next generation JavaScript](https://babeljs.io/)
-   [TypeScript - JavaScript that scales.](http://www.typescriptlang.org/)
-   [HTML5 Cross Browser Polyfills · Modernizr/Modernizr Wiki](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills)
