Express 實戰(一):概覽

BigNerdCoding發表於2017-09-16

2017-08-13-Cover
2017-08-13-Cover

在正式學習 Express 內容之前,我們有必要從大的方面瞭解一下 Node.js 。

在很長的一段時間裡,JavaScript 一門編寫瀏覽器中執行指令碼的語言。不過近些年,隨著網際網路的發展以及技術進步,JavaScript 迎來了一個集中爆發的時代。一個標準性的事件就是 09 年 Node.js 的橫空出世。

Node.js 由 Google Chrome 的 V8 引擎發展而來,它能夠讓 JavaScript 執行在服務端。從而極大的擴充了 JavaScript 的應用場景也讓 JavaScript 全棧成為了一個熱門話題。開發者不必再去學習 Ruby、Python、Java 等語言和框架,僅僅依靠 JavaScript 就能完成前後端的大部分開發任務。

雖然,從某些方面來說 JavaScript 並不完美甚至還有一些設計缺陷,但是這並不影響 Nodejs 的流行。V8 引擎快速處理能力和非同步程式設計風格,讓開發者從多執行緒中解脫了出來。其中,前後端同一技術棧可以說是它最大的殺手鐗。而日益豐富的生態環境也讓 JavaScript 受到開發者越來越多的關注,使用 Node.js 進行開發也變成了一件非常符合潮流的事情了。

和瀏覽器環境中的 JavaScript 一樣,Node.js 也只提供了構建應用基本所需的底層介面和特性。而這些底層介面一般都很冗長、難用。所以有人就在 Node.js 的基礎上實現了 Express 框架。其基本理念與 jQuery 類似,通過對底層介面進行了封裝,在簡化程式碼的同時提供更高階的介面。另外,Express 擴充性也非常強。框架本身與程式的架構和業務無關,並且你還可以通過第三方庫進行功能擴充。

Node.js 應用場景

Node.js (通常簡稱為 Node )是一個 JavaScript 程式碼的執行平臺。雖然大多數情形下 JavaScript 都執行在瀏覽器中,但是並沒有任何地方規定其只能執行在瀏覽器中。作為一門程式語言,本質上它與 Ruby、Python、C++、PHP 並沒有什麼區別。就像你可以使用 python myfile.py 來執行 Python 指令碼,你可以使用 node myfile.js 來執行 JavaScript 程式。

但是 Node 到底有啥優點值得我們在服務端開發是嘗試呢?

首先,Node.js 的 JavaScript 引擎非常快。畢竟,它是基於以速度著稱的 Google Chrome V8 引擎,可以每秒執行幾千條 JavaScript 指令。

其次,Node.js 通過非同步程式設計正規化將其高併發的能力發揮的淋漓盡致。

用現實生活中的烘焙做類比最恰當不過了。假設現在我需要製作一些鬆餅,那麼首先就需要把麵粉弄好。而此時我是無法抽身做其他的事情的。但是,一旦我把鬆餅送進烤箱,我就可以抽身做其他的事情而不必乾等。

在 Node.js 中,客戶端可能隨時都會給服務端傳送請求。有可能在你處理一個請求時,另一個請求也被客戶端送達了。假設,兩個請求都需要訪問資料庫。那麼就可以在第一個請求進行資料庫操作時,轉去處理第二個請求。雖然不能同時對兩者做成響應,但是我們可以使用非同步方式跳過對耗時操作結果的等待直接處理後續請求。而其他的一些執行環境預設是沒有此能力的。例如,Ruby on Rails 同時時間只能處理一個請求。如果想提高程式的併發能力,那麼你就需要去購買更多的伺服器。

下圖可以清晰的看出兩者的區別:

01_01
01_01

與同步方式相比,非同步處理的效率明顯要更高,雖然非同步程式碼也不是並行處理。

當然,這並不是說非同步處理機制讓 Node.js 是世界上最快的語言之一。Node.js 雖然能夠最大化壓制單核 CPU 的效能,但是還是無法與多核處理能力相媲美。其他語言中可以讓你利用多核能力同時執行多個任務。像之前和烘培一樣:你可以購買更多的烤箱來同時烤更多的餅乾。Node 正在開始支援這個能力,但是它並不像其他語言中那樣重要。

就我個人而言,因為效能而選擇 Node.js 並不是最重要的依據,雖然,它確實比 Ruby、Python 等腳步語言要快。最大的理由是,它在前後端開發中使用同一種語言。

通常,在編寫 Web 應用程式時你都會要使用 JavaScript。但是在 Node.js 出現之前,前後端的開發必須使用不同的語言進行。為此你需要學習多種的語言和框架。有了 Node.js 之後,你就可以使用一門語言在前後端開發中自由切換,這是最吸引人的地方。

什麼是 Express ?

Express 是一個基於 Node.js 封裝的上層服務框架,它提供了更簡潔的 API 更實用的新功能。它通過中介軟體和路由讓程式的組織管理變的更加容易;它提供了豐富的 HTTP 工具;它讓動態檢視的渲染變的更加容易;它還定義了一組可擴充標準。

Node.js 的功能

通過一個簡單的 JavaScript 函式,你就可以利用 Node.js 建立一個 Web 程式。該函式用於監聽來自瀏覽器或者其他裝置的發起的網路請求。當接收到一個請求後,函式會分析請求的內容並做成相應的處理。例如,當你請求站點主頁時,該函式就會知道知道你的目的並將主頁的 HTML 渲染出來。如果是請求某個 API 介面,該函式就會把對應的 JSON 資料返回給客戶端。

假設,現在需要伺服器返回當前時間和時區資訊給使用者,那麼該程式大致包括如下功能:

  • 如果客戶端發起主頁請求,Web 應用將會返回一個包含所需資訊的 HTML 。
  • 如果客戶端訪問地址錯誤,Web 應用將會返回 HTTP 404 錯誤,並附帶一段錯誤描述。

如果直接在 Node.js 之上構建該應用而不使用 Express 的話,那麼完整流程圖大抵如下:

01_02
01_02

在上述流程中,開發人員只需要關注圓圈部分內容處理。

這個用於處理瀏覽器請求的 JavaScript 函式叫做請求處理函式(request handler)。它也僅僅是一個處理請求並作出響應的 JavaScript 函式,並無任何特殊之處。Node.js 的 HTTP 服務會接管其中的網路連線,所以你無需關注和處理複雜的網路協議內容。

從程式碼角度來說,該函式包含兩個引數:一個是網路請求 request 物件 ,另一個表示網路響應的 response 物件。在前面時間資訊應用中,該請求處理函式會檢查請求 URL 。如果請求的是主頁,那麼就返回成功的響應頁面。否則,返回 404 錯誤。沒有 Node.js 應用中都是這麼處理的:編寫處理函式對請求作出響應,非常的簡單。

問題在於,Node.js 的 API 對開發者來說並不是非常友好。例如,如果我們想傳送一個 JPEG 圖片的話,可能需要至少 45 行程式碼才行。建立可複用 HTML 模版則更復雜。另外,Node.js 的 HTTP 模組雖然強大,但是仍然缺少一些實用特性。

Express 的出現就是為了解決這些問題,讓你能夠高效的使用 Node.js 來編寫 Web 應用。

Express 給 Node.js 帶來了什麼?

從大的方面來說,Express 為 Node.js 的 HTTP 模組帶來了兩大特性:

  • 通過提供大量易用介面,簡化了程式的複雜度。例如上面放鬆 JPEG 圖片問題,Express 可以將程式碼壓縮帶一行。
  • 它允許對請求處理函式進行拆分,將其重構為很多負責特定請求的小型請求處理函式。這便於模組化和後期維護。

與上圖相比,下圖是 Express 處理請求的大致流程:

01_03
01_03

與之前一樣,開發者只需要關注圓圈部分的內容。

雖然,圖看起來比前面複雜,但是實際開發卻更簡單,本質上它主要做了兩件事:

  1. 與之前一個大型的 request 請求處理函式不同,這裡使用大量小型處理函式。有些函式每次都會執行(例如,請求日誌),而有些函式只在特定情形下才會觸發(例如,404 錯誤)。Express 有很多使用的工具能夠對這些處理函式進行區分。
  2. 請求處理函式中有兩個引數:request 和 response。Node 的 HTTP 可以對其做些基本處理,例如:獲取瀏覽器的 user-agent 。Express 則更為強大,你可以獲取到訪問者 IP 地址,以及解析優化過的 URL 請求物件。esponse 物件也同樣得到了增強。通過類似 sendFile 這樣的函式將檔案傳輸程式碼壓縮至一行。這極大的簡化了處理函式的功能實現。

利用 Express 提供的簡潔 API 介面,通過編寫小型 request 請求處理函式,可以極大的壓縮程式碼量提高開發效率。

Express 的最小化理念

雖然 Express 是一個框架,但是它的程式設計規範非常靈活。你可以用它編寫各種型別的應用,從視訊聊天到個人部落格等等。另外,Express 本身並不是百寶箱,你可能會在實際開發中需要使用大量第三方類庫。這些類庫能幫你解決一些次要問題,這樣你可以將關注點地放在那些重要的問題上,然後發揚 Unix do-one-thing-well 的哲學將事情處理好。

但是這種最小化理念也是一把雙刃劍。一方面,Express 非常靈活可靠,而且不會引入那些無用的垃圾程式碼。另一方面,與其他框架相比這種簡潔不可避免導致了部分功能缺失。這意味需要在程式架構上做更多的功課,並且在出現問題後要花時間去尋找第三方模組。離開箱即用還差一點。

有人喜歡靈活多變的框架,而有人則喜歡那些結構固定的框架。例如,PayPal 雖然也使用 Express,但是卻制定了嚴格的規範來約束其開發者。Express 本身並不關注程式架構,所以程式設計師可以根據偏好自行選擇。由於對程式有著絕對控制權,所以一旦你做出不明智的決策,那麼後面的坑你就慢慢爬吧。

對於大型框架和極簡框架的優劣,從來都沒有固定的正確答案,所以我們無須太過糾結。這裡,我只希望你記住 Express 是一個極簡框架。

Express 的核心

Express 非常簡潔而且對 Node.js 的封裝效果也非常棒,而這一切都源於框架中的四個設計。

中介軟體

正如之前提到的,原生的 Node.js 使用一個 request 處理函式應對所有請求並做出響應。

Middleware 這個名字起的並不好,但是這個術語並不是 Express 獨有,相反它已經存在很久了。概念非常簡單:我們不採用一個巨大的 request 請求處理函式,相反我們將一系列簡單的處理函式組合起來。每一個小的處理函式對應一個小任務,而這些處理函式就被稱為中介軟體。

中介軟體可以處理各種任務,從記錄請求到傳送靜態檔案到設定 HTTP 頭部。例如,應用中使用的第一個中介軟體功能可能就是記錄伺服器中每個請求的 logger-log。當日志記錄完成後,它將繼續執行呼叫鏈中的下一個中介軟體。而下一個中介軟體功能可能會去驗證使用者。如果許可權不夠,就會使用“未授權”進行提示。反之則繼續執行下一個中介軟體。此時中介軟體功能可能會是渲染主頁並結束響應。下圖演示了這兩種情形:

01_04
01_04

在圖中可以看出,記錄日誌的中介軟體位於第一個並且肯定會被執行。緊接著就是執行許可權認證的中介軟體。如果使用者許可權滿足的話就繼續執行下一個中介軟體,否則就不再執行後續中介軟體。

中介軟體最大的特點就是其相對來說比較標準,這也意味著開發者可以通過為 Express 開發中介軟體來擴充其功能。同時,這也許意味著某些通用的中介軟體,很有可能已經有人開發過來,例如: LESS 和 SCSS 等靜態檔案的編譯、許可權控制、cookies 和 sessions 的解析。

路由

相比中介軟體,Routing 顯然更好。與中間價類似,路由對請求處理函式進行了拆分。不同的是,路由根據請求的 URL 和 HTTP 方法來決定處理方式的。

例如,你的程式中有一個主頁和一個留言板頁面。當使用者使用 GET 去請求主頁時,Express 會返回對應的主頁內容。對留言板的請求的處理也是如此。如果使用者通過 POST 方法在留言板頁面中進行了留言操作的話,路由需要做出對應處理並重新整理頁面。

類似於中介軟體,上述路由的處理也是通過處理函式進行定義的。而不同的行為會呼叫不同的處理函式。

Express 中的中介軟體和路由相輔相成。例如:你可以一邊記錄請求日誌,同時對主頁路由做出響應。

子應用

Express 應用通常都很小,甚至可以是一個檔案。隨著應用規模的擴張,可能你會將其進行拆分為多個檔案和資料夾。雖然 Express 對應用的規模增長沒有明確的指導規則。但是我們可以通過 sub-applications 這一重要特性來作出應對。 在 Express 術語中這些只應用也被稱為路由器

通過實現正常規模的路由器子應用,可以將一個大型應用進行模組拆分。甚至你可以對某些子應用進行更細緻的拆分。例如,在應用中可能存在管理後臺、單頁應用、API 介面 等等幾個子模組。這時,你就可以將這些子模組作為一個子應用來實現。詳情如下:

01_05
01_05

當程式規模變大之後,該特性的優勢將會逐步凸顯出來。

易用的 API 函式

Express 由中介軟體和路由構成,在其中你需要編寫大量的非同步處理函式來實現相關功能。

為了使處理過程更加高效正確,Express 在原生的 Node.js 基礎上進行了大量的封裝。在壓縮程式碼量提高效率的同時也降低了人為錯誤的概率。除了傳送檔案之外,Express 提供的 HTML 渲染功能也讓原生 Node.js 望塵莫及。另外在請求解析方面,Express 也做了非常多的工作。

跟前面的特性不一樣的是,在保持功能強大的同時這些易用的函式並不會對應用造成負面影響。

Express 生態環境

像其他的工具一樣,Express 並不是遺世獨立的。

它存活於 Node.js 的生態中,所有你能找到大量的第三發模組提升你的開發效率,例如資料庫連線驅動。因為Express 可擴充性極強,所有整個生態中存在大量為 Express 開發的類庫。

Express 跟其他框架的比較

Express 既不是第一個 Web 框架,當然更不會是最後一個。

同樣,Express 也不是 Node.js 生態中的唯一框架。可能它最大的競爭對手就是 Hapi.js 了。與 Express 類似,它也有路由、中介軟體這就概念。不過不同的是它沒有基於 Node.js 的 HTTP 模組來處理網路請求,而是有 Walmart 開發的網路模組。該模組被 Mozilla,OpenTable 和 NPM 所接受並在真實程式中檢驗過。作為 Express 的最大競爭對手,因此我懷疑雙方的開發人員可能彼此抱有不小敵意。

在 Node.js 世界中最流行的大型框架非全棧式的 Meteor 莫屬。相比自由靈活的 Express ,Metero 有著非常嚴格的程式結構。不同於 Express 只關注 HTTP 層的處理,Meteor 作為全棧框架可以同時執行在客戶端和服務端。當然,這僅僅只是設計上的選擇說不上到底誰更優秀。

跟 Express 基於 Node.js 進行封裝類似,也有人對 Express 進行更高階的封裝。例如,PayPal 創造的 Kraken 。儘管從技術角度來說,Kraken 不過只是 Express 的一箇中介軟體而已。但是,它還是做了不少的事情,例如:中介軟體的安全繫結。Sails.js 則是另一個以 Express 為基礎的新型框架,它內建了資料庫、WebSocket 整合、API 生產等等功能。所有的這些框架,都是 Express 的一種相對固定化的封裝實現。

Express 最主要的幾個特性之一就是中介軟體。Connect 是一個僅作用於 Node.js 和中介軟體的框架。它沒有提供路由和易用函式介面。而 Express 曾經在它的中介軟體層中使用了 Connect。雖然現在被拋棄了,但是 Express 中介軟體一直都完全相容 Connect 中介軟體。這意味著 Express 的火力得到了更大的提升。

除了上面提到的這些,還有很多使用其它語言實現的 Web 框架。

Express 的很多靈感都來自於 Ruby 世界中的輕型 Web 框架 Sinatra。與 Express 類似,Sinatra 中也有路由和中介軟體功能。Sinatra 框架思想被其他很多語言所借鑑並且重新實現,如果你曾經用過這類框架的話,那麼對 Express 一定不會陌生。當然,Express 也借鑑了 Python 中的 Bottle 和 Flask 框架。

相應的,Express 與 Django、Ruby on Rails、ASP.NET 等框架差距巨大。這些框架都十分龐大,而且程式結構也相對固定功能更為豐富。而且,Express 與 PHP 也差異明顯,雖然都是執行在服務上但是後者與 HTML 緊耦合。

Express 僅僅是服務端 Web 框架中的一種,所以我們不能在這裡說 Express 就一定比其他的框架好。它具備一些獨有的特性,例如,Node.js 的效能以及前後端統一的語言。但是與之同時,它的功能遠比其他框架來的少而去 JavaScript 也並不是一門得到廣大開發者認可的“好”語言。脫離具體場景討論優劣勢沒有意義的,下面我們就看看 Express 一些適用場景。

Express 的應用場景

理論上來說,Express 可以用來構建任何 Web 應用。對所有請求作出響應這件事上述提到的框架同樣能夠做到。所以,為什麼要選擇 Express 呢?

最大的優點就是 Node.js 中編寫的 JavaScript 可以在瀏覽器和客戶端實現共享。從程式碼複用角度來說,這種情況是最理想的。從心理角度來說也非常有用,開發過程中無需進行伺服器模式和客戶端模式的切換。前端開發者可以在不學習新語言的情況下直接就可以編寫後端程式碼。當然,前端開發者還是需要學習一些新內容的,不然這本書就沒存在的必要了。

Express 可以幫助你做到這一點,人們已經為這個技術棧擬好了名稱:MEAN 。像 “LAMP” 代表 Linux、Apache、MySQL 和 PHP 一樣,“MEAN” 表示 MongoDB、Express、Angular 和 Node.js。人們之所以對它喜愛有加是因為它是純 JavaScript 技術棧。

Express 常用於驅動單頁應用程式(SPA)。SPA 在前端重度依賴 JavaScript,而且通常需要一個伺服器元件。大多數伺服器只需要簡單的提供 HTML、CSS 和 JavaScript ,但是有時候 REST API 也是常規需求。Express 可以同時完成這兩件任務,既可以提供 HTML 也非常適合構建 API 。 Express 相對較低的學習曲線使得前端開發者在幾乎無需學習新內容的情況下搭建一個 SPA 服務。

當你使用 Express 編寫應用的時候就決定了你必須使用 MEAN 技術棧中的 E 和 N 部分,而對於另外兩個 Express 沒有做出過限定你完全可以採用不同的方案。例如,可以使用 Backbone.js 替換其中的 Angular,構成 MEBN 技術棧。使用 SQL 替換 MongoDB 構成 SEAN 技術棧。雖然 MEAN 術語很常見而且配置也非常流行,但是你完全可以更具需要自行選擇。在這本書中我們將涵蓋的技術棧是 MEN:MongoDB、Express 和 Node.js。

另外 Express 還具有一些實時特性。雖然其他語言也支援 WebSocket 和 WebRTC,但是 Node.js 似乎要更強。這意味著你可以將這些特性應用到 Express 程式中。因為 Node.js 能做的,Express 一樣不少。

Node.js 和 Express 的第三方模組

社群中存在大量可以在 Express 中使用的模組。有一些是 Express 獨佔的,這些模組與路由和中介軟體的特性高度相容。其他的非獨佔模組也能提升 Express 應用的開發體驗和程式效能。

如果你曾經有過 ERB、Jinja2、HAML 等模版引擎的使用經驗的話,你就會發現 Express 自帶的功能在渲染 HTML 方面簡直就是弱雞。好在 Express 還可以使用 EJS 和 Jade 這些社群模版引擎來解決這個問題。

另外,Express 本身並沒有對資料庫做支援。你可以通過檔案、關聯式資料庫、或者其他機制來實現資料儲存。不過後面將會介紹在 Express 中如何通過 Mongoose 來呼叫 MongoDB 資料庫。

需要注意的一點是:並不存在 Express 模組這一概念,所以第三方庫都是 Node.js 模組。Node.js 模組與 Express 相互相容並且能夠配合其工作。所有這些某塊都是在 npm 倉庫註冊的 JavaScript 程式碼塊,並且可以使用一致的方法進行安裝。與其他環境中一樣,模組之間可以相互依賴,並且不同模組可以相互配合。另外,Express 也不過是 Node.js 中的一個模組而已。

Hello World

每次學習新內容的時候,絕大多數都是從 Hello World 開始。

那麼現在我們來看看如何使用 Express 構建一個簡單的 Hello World 工程。不要太關注程式碼的細節,後面將會進行詳細介紹。程式碼如下:

var express = require("express");  #A

var app = express();  #B

app.get("/", function(request, response) {  #C
  response.send("Hello world!");            #C
});                                         #C

app.listen(3000, function() {                       #D
  console.log("Express app started on port 3000."); #D
});                                                 #D複製程式碼

A:匯入 express 模組並新建變數。
B:建立 app 應用
C:設定訪問 Root 路由,並將響應設定為 “Hello world!”。
D:設定程式監聽的埠,並打贏服務啟動成功的資訊。

再次提醒一下,沒有全部弄懂程式碼不要緊,後面會有更詳細的講解。

你馬上就能學到關於 Express 的所有知識了。

小結

本文主要介紹了:

  • Node.js 可以編寫 Web 應用,但是開發過程並不高效。而 Express 則優雅的解決了這些問題。
  • Express 很小但是也非常靈活
  • Express 有幾個關鍵特性:
  • 中介軟體將程式處理進行了拆分並且按照順序鏈式執行。
  • 路由同樣對程式進行了拆分,按規則對不同的訪問請求做出不同的響應。
  • 子工程可以實現對大型 Express 的拆分,從而提高可讀性方便後期維護。
  • Express 中的程式碼大多請求處理函式的編寫,而 Express 為此提供了大量易用 API 。

原文地址

相關文章