Express使用手記:核心入門

  • 2019 年 12 月 5 日
  • 筆記

本文作者:IMWeb 陳映平 原文出處:IMWeb社區 未經同意,禁止轉載

入門簡介

Express是基於nodejs的web開發框架。優點是易上手、高性能、擴展性強。

  • 易上手:nodejs最初就是為了開發高性能web伺服器而被設計出來的,然而相對底層的API會讓不少新手望而卻步。express對web開發相關的模組進行了適度的封裝,屏蔽了大量複雜繁瑣的技術細節,讓開發者只需要專註於業務邏輯的開發,極大的降低了入門和學習的成本。
  • 高性能:express僅在web應用相關的nodejs模組上進行了適度的封裝和擴展,較大程度避免了過度封裝導致的性能損耗。
  • 擴展性強:基於中間件的開發模式,使得express應用的擴展、模組拆分非常簡單,既靈活,擴展性又強。

環境準備

首先,需要安裝nodejs,這一步請自行解決。接著,安裝express的腳手架工具express-generator,這對於我們學習express很有幫助。

npm install -g express-generator

第一個demo

利用之前安裝的腳手架工具,初始化我們的demo項目。

  /tmp mkdir express-demo    /tmp cd express-demo    express-demo express       create : .     create : ./package.json     create : ./app.js     create : ./public     create : ./public/javascripts     create : ./public/images     create : ./public/stylesheets     create : ./public/stylesheets/style.css     create : ./routes     create : ./routes/index.js     create : ./routes/users.js     create : ./views     create : ./views/index.jade     create : ./views/layout.jade     create : ./views/error.jade     create : ./bin     create : ./bin/www       install dependencies:       $ cd . && npm install       run the app:       $ DEBUG=express-demo:* npm start

按照指引,安裝依賴。並啟動服務

npm install

然後,啟動伺服器。

  express-demo  npm start    > [email protected] start /private/tmp/ex1  > node ./bin/www

訪問瀏覽器,邁出成功的第一步。

目錄結構介紹

看下demo應用的目錄結構。大部分時候,我們的應用目錄結構跟這個保持一致就可以了。也可以根據需要自行調整,express並沒有對目錄結構進行限制。

從目錄結構可以大致看出,express應用的核心概念主要包括:路由中間件模板引擎

  express-demo tree -L 1  .  ├── app.js # 應用的主入口  ├── bin  # 啟動腳本  ├── node_modules # 依賴的模組  ├── package.json # node模組的配置文件  ├── public # 靜態資源,如css、js等存放的目錄  ├── routes # 路由規則存放的目錄  └── views # 模板文件存放的目錄    5 directories, 2 files

核心概念簡介

上面提到,express主要包含三個核心概念:路由、中間件、模板引擎。

注意,筆者這裡用的是核心概念這樣的字眼,而不是核心模組,為什麼呢?這是因為,雖然express的中間件有它的定義規範,但是express的內核源碼中,其實是沒有所謂的中間件這樣的模組的。

言歸正傳,三者簡要的來說就是。

  • 中間件:可以毫不誇張的說,在express應用中,一切皆中間件。各種應用邏輯,如cookie解析、會話處理、日誌記錄、許可權校驗等,都是通過中間件來完成的。
  • 路由:地球人都知道,負責定址的。比如用戶發送了個http請求,該定位到哪個資源,就是路由說了算。
  • 模板引擎:負責視圖動態渲染。下面會介紹相關配置,以及如何開發自己的模板引擎。

核心概念:路由

路由分類

粗略來說,express主要支援四種類型的路由,下面會分別舉例進行說明

  1. 字元串類型
  2. 字元串模式類型
  3. 正則表達式類型
  4. 參數類型

分別舉例如下,細節可參考官方文檔

var express = require('express');  var app = express();    // 路由:字元串類型  app.get('/book', function(req, res, next){      res.send('book');  });    // 路由:字元串模式  app.get('/user/*man', function(req, res, next){      res.send('user');  // 比如: /user/man, /user/woman  });    // 路由:正則表達式  app.get(/animals?$/, function(req, res, next){      res.send('animal');  // 比如: /animal, /animals  });    // 路由:命名參數  app.get('/employee/:uid/:age', function(req, res, next){      res.json(req.params);  // 比如:/111/30,返回 {"uid": 111, "age": 30}  });    app.listen(3000);

路由拆分

當你用的應用越來越複雜,不可避免的,路由規則也會越來越複雜。這個時候,對路由進行拆分是個不錯的選擇。

我們分別看下兩段程式碼,路由拆分的好處就直觀的體現出來了。

路由拆分前

var express = require('express');  var app = express();    app.get('/user/list', function(req, res, next){      res.send('/list');  });    app.get('/user/detail', function(req, res, next){      res.send('/detail');  });    app.listen(3000);

這樣的程式碼會帶來什麼問題呢?無論是新增還是修改路由,都要帶著/user前綴,這對於程式碼的可維護性來說是大忌。這對小應用來說問題不大,但應用複雜度一上來就會是個噩夢。

路由拆分後

可以看到,通過express.Router()進行了路由拆分,新增、修改路由都變得極為便利。

var express = require('express');  var app = express();    var user = express.Router();    user.get('/list', function(req, res, next){      res.send('/list');  });    user.get('/detail', function(req, res, next){      res.send('/detail');  });    app.use('/user', user); // mini app,通常做應用拆分    app.listen(3000);

核心概念:中間件

一般學習js的時候,我們都會聽到一句話:一切皆對象。而在學習express的過程中,很深的一個感受就是:一切皆中間件。比如常見的請求參數解析、cookie解析、gzip等,都可以通過中間件來完成。

工作機制

貼上官網的一張圖鎮樓,圖中所示就是傳說中的中間件了。

首先,我們自己編寫一個極簡的中間件。雖然沒什麼實用價值,但中間件就長這樣子。

  • 參數:三個參數,熟悉http.createServer()的同學應該比較眼熟,其實就是req(客戶端請求實例)、res(服務端返回實例),只不過進行了擴展,添加了一些使用方法。
  • next:回調方法,當next()被調用時,就進入下一個中間件。
function logger(req, res, next){      console.log('here comes request');      next();  }

來看下實際例子:

var express = require('express');  var app = express();    app.use(function(req, res, next) {      console.log('1');      next();  });    app.use(function(req, res, next) {      console.log('2');      next();  });    app.use(function(req, res, next) {      console.log('3');      res.send('hello');  });    app.listen(3000);

請求 http://127.0.0.1:3000,看下控制台輸出,以及瀏覽器返回內容。

  middleware git:(master)  node chains.js  1  2  3

應用級中間件 vs 路由級中間件

根據作用範圍,中間件分為兩大類:

  • 應用級中間件
  • 路由級中間件。

兩者的區別不容易說清楚,因為從本質來講,兩類中間件是完全等同的,只是使用場景不同。同一個中間件,既可以是應用級中間件、也可以是路由級中間件。

直接上程式碼可能更直觀。參考下面程式碼,可以簡單粗暴的認為:

  • 應用級中間件:app.use()app.METHODS()介面中使用的中間件。
  • 路由級中間件:router.use()router.METHODS()介面中使用的中間件。
var express = require('express');  var app = express();  var user = express.Router();    // 應用級  app.use(function(req, res, next){      console.log('收到請求,地址為:' + req.url);      next();  });    // 應用級  app.get('/profile', function(req, res, next){      res.send('profile');  });    // 路由級  user.use('/list', function(req, res, next){      res.send('/user/list');  });    // 路由級  user.get('/detail', function(req, res, next){      res.send('/user/detail');  });    app.use('/user', user);    app.listen(3000);

開發中間件

上面也提到了,中間件的開發是是分分鐘的事情,不贅述。

function logger(req, res, next){      doSomeBusinessLogic(); // 業務邏輯處理,比如許可權校驗、資料庫操作、設置cookie等      next();  // 如果需要進入下一個中間件進行處理,則調用next();  }

常用中間件

包括但不限於如下。更多常用中間件,可以點擊 這裡

  • body-parser
  • compression
  • serve-static
  • session
  • cookie-parser
  • morgan

核心概念:模板引擎

模板引擎大家不陌生了,關於express模板引擎的介紹可以參考官方文檔

下面主要講下使用配置、選型等方面的內容。

可選的模版引擎

包括但不限於如下模板引擎

  • jade
  • ejs
  • dust.js
  • dot
  • mustache
  • handlerbar
  • nunjunks

配置說明

先看程式碼。

// view engine setup  app.set('views', path.join(__dirname, 'views'));  app.set('view engine', 'jade');

有兩個關於模版引擎的配置:

  1. views:模版文件放在哪裡,默認是在項目根目錄下。舉個例子:app.set('views', './views')
  2. view engine:使用什麼模版引擎,舉例:app.set('view engine', 'jade')

可以看到,默認是用jade做模版的。如果不想用jade怎麼辦呢?下面會提供一些模板引擎選擇的思路。

選擇標準

需要考慮兩點:實際業務需求、個人偏好。

首先考慮業務需求,需要支援以下幾點特性。

  • 支援模版繼承(extend)
  • 支援模版擴展(block)
  • 支援模版組合(include)
  • 支援預編譯

對比了下,jadenunjunks都滿足要求。個人更習慣nunjunks的風格,於是敲定。那麼,怎麼樣使用呢?

支援nunjucks

首先,安裝依賴

npm install --save nunjucks

然後,添加如下配置

var nunjucks = require('nunjucks');    nunjucks.configure('views', {      autoescape: true,      express: app  });    app.set('view engine', 'html');

看下views/layout.html

<!DOCTYPE html>  <html>  <head>      <title>          {% block title %}              layout title          {% endblock %}      </title>  </head>  <body>  <h1>      {% block appTitle %}          layout app title      {% endblock %}  </h1>  <p>正文</p>    </body>  </html>

看下views/index.html

{% extends "layout.html" %}  {% block title %}首頁{% endblock %}  {% block appTitle %}首頁{% endblock %}

開發模板引擎

通過app.engine(engineExt, engineFunc)來註冊模板引擎。其中

  • engineExt:模板文件後綴名。比如jade
  • engineFunc:模板引擎核心邏輯的定義,一個帶三個參數的函數(如下)
// filepath: 模板文件的路徑  // options:渲染模板所用的參數  // callback:渲染完成回調  app.engine(engineExt, function(filepath, options, callback){        // 參數一:渲染過程的錯誤,如成功,則為null      // 參數二:渲染出來的字元串      return callback(null, 'Hello World');  });

比如下面例子,註冊模板引擎 + 修改配置一起,於是就可以愉快的使用後綴為tmpl的模板引擎了。

app.engine('tmpl', function(filepath, options, callback){        // 參數一:渲染過程的錯誤,如成功,則為null      // 參數二:渲染出來的字元串      return callback(null, 'Hello World');  });  app.set('views', './views');  app.set('view engine', 'tmpl');

相關鏈接

模板引擎對比:點擊這裡

express模版引擎介紹:點擊這裡

開發模版引擎:點擊這裡

更多內容

前面講了一些express的入門基礎,感興趣的同學可以查看官方文檔。篇幅所限,有些內容在後續文章展開,比如下面列出來的內容等。

  • 進程管理
  • 會話管理
  • 日誌管理
  • 性能優化
  • 調試
  • 錯誤處理
  • 負載均衡
  • 資料庫支援
  • HTTPS支援
  • 業務實踐
  • 。。。

相關鏈接

express官網:http://expressjs.com/