前言
在前一篇文章中,我們談到如何安裝 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.json 和 package-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 程式,所以在開發工具的選擇上很簡單,目前只有三個工具:
一開始先將專案初始化:
$ 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 程式,所以只用最精簡的配置。如果想要用這個樣板專案來寫網頁後端程式,最好以此專案為基礎,再建一個自己的樣板專案。