位元詩人 現代 [JavaScript] 程式設計教學:利用 Babel 支援現代 JavaScript 的特性

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

在前一篇文章中,我們談到如何安裝 Node.js 本身,本文則介紹如何安裝 Babel。

Babel 是 JavaScript 轉譯器,可將 ES6+ 程式碼轉為等效的 ES5 程式碼,我們在撰寫程式時就可以使用新的語法特性來改善程式碼的品質,而不用刻意守在舊有的手法。在實際執行 JavaScript 程式時,則會以普遍受到支援的 ES5 來執行,不用考慮 ES6 實作不夠完整的議題。

或許再過幾年,瀏覽器充份支援 ES6 的特性,那麼 Babel 專案就可以功成身退。但我們的專案並沒有白費,因為我們原本就是用 JavaScript 來寫程式,只是在版本間做了些轉換。

安裝 Babel

Babel 本身以 NPM 套件發布,透過 NPM 即可安裝。Babel 官網提供許多種安裝方式,此處我們以 NPM 來安裝。

首先,我們建立一個新的 npm 專案,npm 專案以單一目錄為基礎:

$ mkdir myapp
$ cd myapp
$ npm init -y

我們這裡直接接受預設值,因為這些設定日後都可以再修改,一開始不用太費心在這裡。設定完成後,npm 會為我們產生 package.jsonpackage-lock.json ,這是 npm 套件的設定檔,以 JSON 格式撰寫。

接著,安裝 Babel 命令列工具:

$ npm install --save-dev @babel/core @babel/cli

這行命令的意思是在專案資料夾內以局部安裝模式安裝 babel-cli 套件。

npm 套件的安裝有兩種方式,一種是全域安裝,一種是局部安裝,前者會將套件安裝在系統上,後者則是安裝在專案資料夾內的 node_modules 資料夾;透過 npm 的設計,使用相同的 package.json 設定檔,在不同機器上可根據此設定檔快速重建相同的開發環境。

--save-dev 的意思是該套件僅在開發時會用到,實際在發布專案時不需該套件。

接著,我們要修改 package.json ,讓 npm 工具呼叫 Babel 工具:

{
  "scripts": {
    "build": "babel src -d lib"
  },
  ...
}

關鍵在於將 babel src -d lib 這行指令寫入此專案的設定檔,之後就可以透過 npm 呼叫 Babel。

Babel 是以 plugin 的方式散布,一開始時,我們尚未安裝任何 plugin,這時 Babel 不會自動轉換 ECMAScript 程式碼。使用 npm 安裝 plugin:

$ npm install @babel/preset-env --save-dev

另外,還要設定 .babelrc 設定檔:

{
  "presets": ["@babel/preset-env"]
}

之後,我們開始撰寫 Babel 程式碼:

$ mkdir src
$ touch src/app.js

撰寫程式碼如下,在以下程式碼中,字串模板 (string template) 是 ECMAScript 6 才出現的新語法:

function greet(message) {
    return `Hello ${message}`;
}

console.log(greet('World'));

使用 Babel 轉換程式碼:

$ npm run build

使用 node 執行轉換後的程式碼:

$ node lib/app.js
Hello World

為了簡化日後的工作流程,我們再度修改 package.json

{
  (省略一些內容...)
  "scripts": {
    "build": "babel src -d lib",
    "start": "node lib/app.js",
    "prestart": "npm run build"
  },
  (省略一些內容...)
}

我們加入兩個指令,一個是 start,一個是 prestart,前者可以呼叫 node 來執行轉換後的 JavaScript 程式碼,後者是 start 指令的 hook,可在每次執行 start 前預先編譯專案程式碼。之後,我們只要用一行指令即可編譯並執行 ECMAScript 程式碼:

$ npm start
(省略一些訊息...)
Hello World

(選擇性) 安裝 Flow

Flow 是 JavaScript 靜態程式碼檢查器,使用方式是在 JavaScript 程式碼內加入額外的型別資訊,在執行程式前會先對程式碼進行靜態分析,協助程式設計者挑出一些錯誤。

由於 JavaScript 本身沒有這些型別相關的資訊,最後這些型別資訊會抹除掉,上線的 JavaScript 程式碼就和一般的 JavaScript 程式碼無異;其實 TypeScript 也是使用類似的方法在處理型別檢查,由 TypeScript 轉換後的 JavaScript 程式碼也會抹除型別相關的資訊,所以 flow 某種程度上補足了 Babel 不足的地方。

先安裝給 Babel 用的 Flow 套件:

$ npm install --save-dev @babel/preset-flow

修改 .babelrc

{
  "presets": ["@babel/preset-flow", "@babel/preset-env"]
}

babel-preset-flow 套件的作用是將 JavaScript 程式碼中有關型別的標註抹去,而非進行型別檢查。我們還要另外安裝 Flow 才會有型別檢查的功能:

$ npm install --save-dev flow-bin

package.json 中撰寫相關 hook:

{
  (省略一些設定...)
  "scripts": {
    "flow": "flow",
    "build": "babel src -d lib",
    "start": "node lib/app.js",
    "prestart": "flow && npm run build"
  },
  (省略一些設定...)
}

第一次在專案中執行 flow 時需初始化:

$ npm run flow init

接著,設置 .flowconfig

[ignore]
.*/node_modules/.*

[include]
./src

[libs]

[lints]

[options]

[strict]

記得將 node_modules/ 加入忽略清單,以免 flow 對一堆 NPM 套件進行不必要的檢查。

寫完 hook 後,執行專案時即可一步到位:

$ npm start

我們寫一個簡短的例子,實際體驗 flow 的功能:

// @flow
function greet(name: string):string {
    return `Hello ${name}`;
}

console.log(greet(12345));

此程式引發以下錯誤訊息:

Error: src/app.js:6
  6: console.log(greet(12345));
                       ^^^^^ number. This type is incompatible with the expected param type of
  2: function greet(name: string):string {
                          ^^^^^^ string

(省略一些訊息...)

將程式修改如下:

// @flow
function greet(name: string):string {
    return `Hello ${name}`;
}

console.log(greet('World'));

程式可正確執行:

$ npm start 
(省略一些訊息...)
Hello World

雖然 Flow 不是 Babel 必備的部分,在專案中引入 Flow 的確可以藉由靜態程式碼檢查來改善程式碼的品質;讀者可自行考慮是否要在 Babel 專案中加入 Flow。

(選擇性) 使用 JavaScript 樣版專案寫 JavaScript 程式

本文大部分的內容都在說明如何建置使用 Babel 和 Flow 的 Node.js 專案。這是因為我們有時候要在現有的網頁程式專案中加上 Babel 和 Flow 的支援。如果每次開一個新的 Node.js 專案,都得重新做一次這樣的過程,也未免太不經濟了。

由於 Node.js 生態圈的開發工具繁多,而且變動快速,要做出包山包海的專案產生器 (project generator) 過於困難。退而求其次,我們可以看到很多的樣板專案 (boilerplate project)。這些樣板專案會預先選好開發工具和函式庫,並做好初步的設置,我們就可以省下一些重覆的工作。

但 Node.js 生態圈的工具太多,每位開發者的喜好不同,別人寫好的樣板專案不一定符合自己的需求。最好能夠下載別人預寫好的樣板專案,加以修改一番後,變成自己的樣板專案。筆者以為,每個網頁程式開發團隊應該都要維護一份自己的樣板專案,明確規範團隊所使用的 JavaScript 函式庫和開發工具。

如果讀者還不熟悉建置樣板專案的過程,可以先參考筆者所建的樣板專案。這個專案是用來寫純 JavaScript 程式。如果想要拿來寫後端程式,最好自己下載一份後另外加上 Express.js 或其他 Node.js 網頁框架的程式碼,然後再建一個自己的後端程式樣板專案。

(附註) 使用 nodejs-boilerplate 寫 JavaScript 程式

nodejs-boilerplate 是筆者所製作的樣板專案,目的是簡化初期設置 JavaScript 專案的時間。如果讀者覺得沒有這個需求,可以略過本節不看。

這個樣板專案的目標是寫純 JavaScript 程式,所以在開發工具的選擇上很簡單,目前只有三個工具:

  • Babel:JavaScript 轉譯器
  • Flow:對 JavaScript 程式碼進行 (選擇性的) 型別檢查
  • ESLint:檢查 JavaScript 程式碼的品質

一開始先將專案初始化:

$ git clone https://github.com/cwchentw/nodejs-boilerplate.git
$ mv nodejs-boilerplate myapp
$ cd myapp
$ npm install

實際編輯的檔案位於 src/app.js 。在這裡,我們可以放心地用 ES6+ 的 JavaScript 語法特性來寫程式,也可以加入 Flow 的型別標註。寫好的程式還會自動用 ESLint 檢查程式碼品質。

我們假定已經編輯好了,要存檔時得修改遠端主機的位置。參考以下指令:

$ git remote set-url origin path/to/remote/repo
$ git push

執行本專案時,有分開發者模式 (development mode) 和正式釋出模式 (release mode)。輸入以下指令以開發者模式編譯檔案:

$ npm run start-dev

在開發者模式中,編好的檔案 dist/app.js 會排列整齊,可以觀看編出來的檔案為何。我們不建議手動編輯輸出,但有時要看一下實際的輸出才知道問題出在那裡。

輸入以下指令以正式釋出模式編譯檔案:

$ npm start

在正式釋出模式中,輸出會經縮小器縮小,可節約磁碟空間,但不利於閱讀原始碼。

這個樣板專案的目標是寫純 JavaScript 程式,所以只用最精簡的配置。如果想要用這個樣板專案來寫網頁後端程式,最好以此專案為基礎,再建一個自己的樣板專案。

關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。