位元詩人 [Node.js] 程式設計教學:用於網頁程式的套件

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

Node.js 有許多網頁程式相關的開發工具,可以協助網頁程式的開發。由於 JavaScript 生態圈的工具繁多,無法在一篇文章的容量講完。本文先講概念的部分,後續的文章會實際動手建立一個網頁程式的樣板專案。

原本的網頁程式使用什麼語言並不是重點,本文的概念是在網頁程式中額外加上以 Node.js 運行的網頁工具,這些工具只處理網頁前端的部分。前端和後端可以放在同一個專案中,也可以拆成兩個專案,下文會討論兩者的差別。

為什麼沒有網頁程式專案生成器?

在現代語言中,程式專案生成器是相當實用的部分。除了節約一開始建置專案的心力外,標準化的專案架構可以讓開發團隊協作起來更加容易。由於專案生成器需事先規畫其架構,除非該語言在剛問世時就設計好這個部分,大部分語言的專案生成器都是後設的功能。

我們可以在網路上找到許多網頁程式的樣板專案,卻沒有標準化的專案產生器。這是因為 JavaScript 原本是內嵌在網頁中的腳本語言,當時根本沒想到會有 Node.js 這樣的 JavaScript 運行環境出現。現在看到的 JavaScript 開發工具都是後設的軟體,原本並沒有這些好用的工具。

由於 Node.js 生態圈的開發工具繁多,目前在工具選擇上尚未形成足夠的共識。所以樣板專案的架構五花八門,程式人得慢慢找尋適合自己的專案架構,或是自己再從頭刻一個樣板專案。

是否要前後端分離?

傳統上,網頁程式是以後端語言為主。前端的代碼是以靜態資源的形式依附在專案中。前端程式的角色相對不重要,只是做一些簡單的運算或表單驗證等。輸出網頁程式的介面是由後端程式來負責。

由於終端裝置在效能上較先前進步,現在的網頁程式會把生成介面的任務移到前端程式來處理。後端程式的角色變成資料的運算和傳遞。原本網頁程式受到同源政策 (same origin policy) 的限制,前後端程式得綁在一起。後來透過 CORS 特性的發展,前後端專案可以拆開,前後端網頁程式可放在不同的網域中。

CORS 在現代瀏覽器 (modern browsers) 上都有支援,除非專案得支援舊瀏覽器,可放心地將專案重構成前後端分離的架構。

所有的網頁工具應以 Node.js 和 NPM 來運行

在 C 或 C++ 這類傳統語言中,除了編譯器以外,其他的工具是獨立存在的軟體。像是管理專案的 Make 或是檢查程式碼的 CppCheck 等。

但這些開發工具不一定存在於每個系統上。當開發者換了個系統,就得將這些開發工具一一移植到新系統上。不一定每個開發工具都能順利移植到其他系統上,這個現象造成開發工具的歧異。

在現代語言中,會傾向將所有的開發工具都使用該語言實作。表面上程式人在重造輪子,但日後要移植該語言到新的系統上時,只要移植運行環境即可,其他的開發工具即可自動在新系統上使用。

以網頁程式相關的開發工具來說,Node.js 為基礎的生態圈相當成功,幾乎所有的功能都可在 NPM 套件庫找到相對應的套件,網頁程式設計師不用再建置另一個語言的開發環境。

用 HTML 模板語言 (Template Language) 做為網頁的架構

HTML 文件本身是靜態的文件,並沒有程式邏輯的功能,內容 (content) 和版面 (layout) 會直接寫在同一份文件中。使用 HTML 模板的目的在於動態地在網頁中加入內容,讓內容和版面可以分離。

早期最知名的 HTML 模板語言是 PHP,這個語言知名到被濫用的程度。原本 PHP 只是用來搭配 CGI 程式的 HTML 模板語言,但是大家都把程式邏輯直接寫在 PHP 程式碼中,沒人想去寫 CGI 的部分 (參考這裡)。現在 PHP 被視為一種通用型語言,就像 Python 或 Ruby 般,甚至還有用 PHP 寫的 HTML 模板語言 (WTF?)。

現在新的 HTML 模板語言不會再走 PHP 的老路,這些 HTML 模板語言的功能比較簡單,只是用來將內容和版面掛勾,複雜的程式邏輯則交由其他程式去處理。一些 HTML 模板的例子有 PugJadeHandlebarsNunjucks 等。

用 CSS 前置處理器 (Preprocessor) 寫程式化的 CSS

CSS 本身是靜態的設定檔,沒有程式語言的功能。使用 CSS 前置處理器的目的是使用程式語言的特性去寫 CSS,讓撰寫 CSS 的任務更加容易。瀏覽器無法讀取 CSS 前置處理器所用的代碼,網頁程式要運作前要先將這些 CSS 前置處理器代碼轉成原生 CSS 代碼。

CSS 前置處理器有好幾種,各自在功能上有一些差異。比較知名的 CSS 前置處理器有 SassLess CSSStylus 等。由於這些 CSS 前置處理器在功能上是重疊的,只要選一個來用即可,不需每套都學。

用 JavaScript 轉譯器 (Transcompiler) 優化網頁程式代碼

由於 JavaScript 在核心語法上有一些缺失,現在的程式人很少用原生 JavaScript 寫網頁程式,而會使用 JavaScript 轉譯器來寫 (參考這裡)。程式碼轉譯器的原理在於使用較好的語言寫程式,再用轉譯器自動轉換成等效的目標語言。透過程式碼轉譯器,我們不受限於目標語言,可以使用自己偏好的語法來寫程式。

JavaScript 轉譯器很多種,筆者目前最推薦 BabelTypeScript。這兩者所採用的語言皆相容於原生 JavaScript 但加入一些改善的部分。由於這些轉譯器的語法相容於原生 JavaScript,我們可以直接用在現有的專案上,然後再慢慢重構到較好的語法上。若轉譯器的語法不相容於 JavaScript,我們只能移植或改寫,需耗費更多時間。

用打包程式 (Bundler) 合併多個靜態資源

傳統的網頁程式中的靜態資源 (CSS、JavaScript 等) 會分散在多個檔案中。但這樣的配置方式,需要用多個請求 (request) 才能逐一下載資源,無形中消耗了網頁程式的效能。透過打包程式 (bundler),可以將靜態資源合併,以節約請求次數。

JavaScript 原本沒有模組的概念,所有的 JavaScript 程式碼共用同一個全域命名空間。後來在前端和後端各自發展出一些模組程式,將 JavaScript 程式碼做適當的區隔。前端的模組程式有 RequireJSSystemJS 等。前後端共用的模組程式有 AMDUMD 等。

打包程式在打包模組的時候,可以自動在 JavaScript 程式碼中加入 AMD 或 UMD 的樣板程式碼,不需要手動撰寫這部分的代碼。

以任務執行器 (Task Runner) 觸發各式各樣的任務

由於很多 Node.js 開發工具是以函式庫的形式來發佈,使用 Node.js 環境另寫一個任務執行器來呼叫這些開發工具就成了最適宜的選項。只是 Node.js 生態圈的任務執行器不只一種,反而增加了程式人的負擔。由於任務執行器在功能上大抵雷同,只是介面和設置方式相異,只要開發團隊成員間取得共識,共用同一種任務執行器即可。

目前常見的任務執行器有 GruntGulpwebpackParcel 等。有時候任務執行器兼有打包程式的功能,因為 Node.js 的任務執行器可藉由外掛 (plugin) 擴展其功能。

結語

在本文中,我們介紹了網頁程式所需的功能和相對應的套件,相信可以給讀者建立一些概念。由於 Node.js 生態圈變動快速,套件有可能過一陣子就過時,但概念的變遷其實沒那麼大。在學習這些工具時,重點在於該工具為什麼會出現?解決了什麼問題?是否有必要性?這樣才不會落入不必要的追新循環中。

關於作者

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

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