欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

node.js Web應(yīng)用框架Express入門指南

 更新時(shí)間:2014年05月28日 09:16:52   作者:  
這篇文章主要介紹了node.js Web應(yīng)用框架Express入門指南,從安裝到各種技術(shù)的應(yīng)用,都進(jìn)行了講解,是一篇不錯(cuò)的Express入門教程,需要的朋友可以參考下

一、安裝

復(fù)制代碼 代碼如下:
$ npm install express

或者在任何地方使用可執(zhí)行的 express(1) 安裝:

復(fù)制代碼 代碼如下:
\# 譯注:強(qiáng)烈建議這種方式
$ npm install -g express

二、快速上手

最快上手 express 的方法是利用可執(zhí)行的 express(1) 來生成一個(gè)應(yīng)用,如下所示:

創(chuàng)建一個(gè) app:

復(fù)制代碼 代碼如下:

$ npm install -g express
$ express /tmp/foo && cd /tmp/foo

安裝依賴包:
復(fù)制代碼 代碼如下:

$ npm install -d

啟動(dòng)服務(wù)器:
復(fù)制代碼 代碼如下:

$ node app.js

三、創(chuàng)建一個(gè)服務(wù)器

要?jiǎng)?chuàng)建一個(gè) express.HTTPServer 實(shí)例,只需調(diào)用 createServer() 方法。 通用這個(gè)應(yīng)用實(shí)例,我們可以定義基于 HTTP 動(dòng)作(HTTP Verbs)的路由,以 app.get() 為例:

復(fù)制代碼 代碼如下:

var app = require('express').createServer();

app.get('/', function(req, res){
  res.send('hello world');
});

app.listen(3000);

四、創(chuàng)建一個(gè) HTTPS 服務(wù)器

如上述初始化一個(gè) express.HTTPSServer 實(shí)例。然后我們給它傳一個(gè)配置對(duì)象,接受 key、cert 和其他在 https 文檔 所提到的(屬性/方法)。

復(fù)制代碼 代碼如下:

 var app = require('express').createServer({ key: ... });

五、配置

Express 支持任意環(huán)境,如產(chǎn)品階段(production)和開發(fā)階段(development)。開發(fā)者可以使用 configure() 方法來設(shè)置當(dāng)前所需環(huán)境。如果 configure() 的調(diào)用不包含任何環(huán)境名,它將運(yùn)行于所有環(huán)境中所指定的回調(diào)。

譯注: 像 production / development / stage 這些別名都是可以自已取的,如 application.js 中的 app.configure 所示。實(shí)際用法看下面例子。

下面這個(gè)例子僅在開發(fā)階段 dumpExceptions (拋錯(cuò)),并返回堆棧異常。不過在兩個(gè)環(huán)境中我們都使用 methodOverride 和 bodyParser。注意一下 app.router 的使用,它可以(可選)用來加載(mount)程序的路由,另外首次調(diào)用 app.get()、app.post() 等也將會(huì)加載路由。

復(fù)制代碼 代碼如下:

app.configure(function(){
    app.use(express.methodOverride());
    app.use(express.bodyParser());
    app.use(app.router);
});

app.configure('development', function(){
    app.use(express.static(__dirname + '/public'));
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  var oneYear = 31557600000;
  app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
  app.use(express.errorHandler());
});


對(duì)于相似的環(huán)境你可以傳遞多個(gè)環(huán)境字符串:
復(fù)制代碼 代碼如下:

app.configure('stage', 'prod', function(){
  // config
});

對(duì)于任何內(nèi)部設(shè)置(#),Express 提供了 set(key[, val])、 enable(key) 和 disable(key) 方法:

譯注:設(shè)置詳見:application.js 的 app.set。

復(fù)制代碼 代碼如下:

 app.configure(function(){
    app.set('views', __dirname + '/views');
    app.set('views');
    // => "/absolute/path/to/views"

    app.enable('some feature');
    // 等價(jià)于:app.set('some feature', true);

    app.disable('some feature');
    // 等價(jià)于:app.set('some feature', false);

    app.enabled('some feature')
    // => false
 });


變更環(huán)境我們可以設(shè)置 NODE_ENV 環(huán)境變量,如:
復(fù)制代碼 代碼如下:

$ NODE_ENV=production node app.js

這非常重要,因?yàn)槎鄶?shù)緩存機(jī)制只在產(chǎn)品階段是被打開的。

六、設(shè)置

Express 支持下列快捷(out of the box)設(shè)置:

1.basepath 用于 res.redirect() 的應(yīng)用程序基本路徑(base path),顯式地處理綁定的應(yīng)用程序(transparently handling mounted apps.)
2.view View 默認(rèn)的根目錄為 CWD/views
3.view engine 默認(rèn) View 引擎處理(View 文件)并不需要使用后綴
4.view cache 啟用 View 緩存 (在產(chǎn)品階段被啟用)
5.charet 改變編碼,默認(rèn)為 utf-8
6.case sensitive routes 路由中區(qū)分大小寫
7.strit routing 啟用后(路由中的)結(jié)尾 / 將不會(huì)被忽略(譯注:即 app.get('/sofish') 和 app.get('/sofish/') 將是不一樣的)
8.json callback 啟用 res.send() / res.json() 顯式的 jsonp 支持(transparent jsonp support)

七、路由

Express 利用 HTTP 動(dòng)作提供一套提示性強(qiáng)、有表現(xiàn)力的路由 API。打個(gè)比方,如果想要處理某個(gè)路徑為 /user/12 的賬號(hào),我們能像下面這樣來定義路由。關(guān)聯(lián)到命名占位符(named placeholders)的值可用 req.params 來訪問。

復(fù)制代碼 代碼如下:

app.get('/user/:id', function(req, res){
    res.send('user ' + req.params.id);
});

路由是一個(gè)在內(nèi)部被編譯為正則的字符串。譬如,當(dāng) /user/:id 被編譯,一個(gè)簡(jiǎn)化版本的正則表達(dá)弄大概如下:
復(fù)制代碼 代碼如下:

// 修改一下官方的這個(gè)字符串
/\/user\/([^\/]+)\/?/

正則表達(dá)式可以傳入應(yīng)用于復(fù)雜的場(chǎng)景。由于通過字面量正則表達(dá)式捕獲的內(nèi)容組是匿名的,我們可能直接通過 req.params 來訪問它們。因此,我們捕獲的第一組內(nèi)容將是 req.params[0],同時(shí)第二組是緊接著的 req.params[1]。
復(fù)制代碼 代碼如下:

app.get(/^\/users?(?:\/(\d+)(?:\.\.(\d+))?)?/, function(req, res){
    res.send(req.params);
});

Curl 針對(duì)上述定義路由的請(qǐng)求:
復(fù)制代碼 代碼如下:

$ curl http://dev:3000/user
[null,null]
$ curl http://dev:3000/users
[null,null]
$ curl http://dev:3000/users/1
["1",null]
$ curl http://dev:3000/users/1..15
["1","15"]

下面是一些路由的實(shí)例,關(guān)聯(lián)到他們可能使用到的路徑:
復(fù)制代碼 代碼如下:

"/user/:id"
/user/12

"/users/:id?"
/users/5
/users

"/files/*"
/files/jquery.js
/files/javascripts/jquery.js

"/file/*.*"
/files/jquery.js
/files/javascripts/jquery.js

"/user/:id/:operation?"
/user/1
/user/1/edit

"/products.:format"
/products.json
/products.xml

"/products.:format?"
/products.json
/products.xml
/products

"/user/:id.:format?"
/user/12
/user/12.json


舉個(gè)例子,我們可以使用 POST 發(fā)送 json 數(shù)據(jù),通過 bodyParser 這個(gè)可以解析 json 請(qǐng)求內(nèi)容(或者其他內(nèi)容)的中間件來返回?cái)?shù)據(jù),并將返回結(jié)果存于 req.body 中:
復(fù)制代碼 代碼如下:

var express = require('express')
  , app = express.createServer();

app.use(express.bodyParser());

app.post('/', function(req, res){
  res.send(req.body);
});

app.listen(3000);


通常我們可以使用一個(gè)像 user/:id 這樣,沒有(命名)限制的“傻瓜”式的占位符。然而比方說,我們要限制用戶 id 只能是數(shù)字,那么我們可能使用 /user/:id([0-9]+),這個(gè)將僅當(dāng)占位符是包含至少一位數(shù)字時(shí)才生效(適配,match)。

八、進(jìn)路控制(Passing Route Control)

我們可以通過調(diào)用第三個(gè)參數(shù),next() 函數(shù),來控制下一個(gè)適配的路由。如果找不到適配,控制權(quán)將會(huì)傳回給 Connect,同時(shí)中間件將會(huì)按在 use() 中添加的順序被依次調(diào)用。道理同樣適應(yīng)于多個(gè)定義到同一路徑的路由,他們將會(huì)依次被調(diào)用直到其中某個(gè)不調(diào)用 next() 而決定做出請(qǐng)求響應(yīng)。

復(fù)制代碼 代碼如下:

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next();
    }
});

app.get('/users', function(req, res){
    // do something else
});


app.all() 方法只調(diào)用一次就可以方便地把同樣的邏輯到所有 HTTP 動(dòng)作。下面我們使用它來從偽數(shù)據(jù)中提取一個(gè)用戶,將其賦給 req.user。
復(fù)制代碼 代碼如下:

var express = require('express')
  , app = express.createServer();

var users = [{ name: 'tj' }];

app.all('/user/:id/:op?', function(req, res, next){
  req.user = users[req.params.id];
  if (req.user) {
    next();
  } else {
    next(new Error('cannot find user ' + req.params.id));
  }
});

app.get('/user/:id', function(req, res){
  res.send('viewing ' + req.user.name);
});

app.get('/user/:id/edit', function(req, res){
  res.send('editing ' + req.user.name);
});

app.put('/user/:id', function(req, res){
  res.send('updating ' + req.user.name);
});

app.get('*', function(req, res){
  res.send(404, 'what???');
});

app.listen(3000);


九、中間件

使用的 Connect 中間件(屬性)通常伴隨著你的一個(gè)常規(guī) Connect 服務(wù)器,被傳到 express.createServer() 。如:

復(fù)制代碼 代碼如下:

var express = require('express');

var app = express.createServer(
      express.logger()
    , express.bodyParser()
  );


另外,在 configure() 塊內(nèi) —— 這個(gè)漸進(jìn)式的宮殿(譯注:笑^^,in a progressive manner),我們還可以方便地使用 use() 來添加中間件。
復(fù)制代碼 代碼如下:

app.use(express.logger({ format: ':method :url' }));

通常,使用 connect 中間件你可能會(huì)用到 require('connect'),像這樣:
復(fù)制代碼 代碼如下:

var connect = require('connect');
app.use(connect.logger());
app.use(connect.bodyParser());

這在某種程度上來說有點(diǎn)不爽,所以 express 重導(dǎo)出(re-exports)了這些中間件屬性,盡管他們是一樣的:
復(fù)制代碼 代碼如下:

app.use(express.logger());
app.use(express.bodyParser());

中間件的順序非常重要,當(dāng) Connect 收到一個(gè)請(qǐng)求,我們傳到 createServer() 或者 use() 執(zhí)行的第一個(gè)中間件將附帶三個(gè)參數(shù),request、response,以及一個(gè)回調(diào)函數(shù)(通常是 next)。當(dāng) next() 被調(diào)用,將輪到第二個(gè)中間件,依此類推。之所以說這是值得注意的,是因?yàn)楹芏嘀虚g件彼此依賴,例如 methodOverride() 查詢 req.body 方法來檢測(cè) HTTP 方法重載,另一方面 bodyParser() 解析請(qǐng)求內(nèi)容并將其于寄存于 req.body。另一個(gè)例子是 cookie 解析和 session 支持,我們必須先 use() cookieParser() 緊接著 session()。

很多 Express 應(yīng)用都包含這樣的一行 app.use(app.router),這看起來可能有點(diǎn)奇怪,其實(shí)它僅僅是一個(gè)包含所有定義路由規(guī)則,并執(zhí)行基于現(xiàn)有 URL 請(qǐng)求和 HTTP 方法路由查找的一個(gè)中間件功能。Express 允許你決定其位置(to position),不過默認(rèn)情況下它被放置于底部。通過改變路由的位置,我們可以改變中間件的優(yōu)先級(jí),譬如我們想把錯(cuò)誤報(bào)告做為最后的中間件,以便任何傳給 next() 的異常都可以通過它來處理;又或者我們希望靜態(tài)文件服務(wù)優(yōu)先級(jí)更低,以允許我們的路由可以監(jiān)聽單個(gè)靜態(tài)文件請(qǐng)求的下載次數(shù),等等。這看起來差不多是這樣的:

復(fù)制代碼 代碼如下:

app.use(express.logger(...));
app.use(express.bodyParser(...));
app.use(express.cookieParser(...));
app.use(express.session(...));
app.use(app.router);
app.use(express.static(...));
app.use(express.errorHandler(...));

首先我們添加 logger(),它可能包含 node 的 req.end() 方法,提供我們響應(yīng)時(shí)間的數(shù)據(jù)。接下來請(qǐng)求的內(nèi)容將會(huì)被解析(如果有數(shù)據(jù)的話),緊接著的是 cookie 解析和 session 支持,同時(shí) req.session 將會(huì)在觸發(fā) app.router 中的路由時(shí)被定義,這時(shí)我們并不調(diào)用 next(),因此 static() 中間件將不會(huì)知道這個(gè)請(qǐng)求,如若已經(jīng)定義了如下一個(gè)路由,我們則可以記錄各種狀態(tài)、拒絕下載和消耗下載點(diǎn)數(shù)等。
復(fù)制代碼 代碼如下:

var downloads = {};

app.use(app.router);
app.use(express.static(__dirname + '/public'));

app.get('/*', function(req, res, next){
  var file = req.params[0];
  downloads[file] = downloads[file] || 0;
  downloads[file]++;
  next();
});

十、路由中間件

路由可以利用路由器中間件,傳遞一個(gè)以上的回調(diào)函數(shù)(或者數(shù)組)到其方法中。這個(gè)特性非常有利于限制訪問、通過路由下載數(shù)據(jù),等等。

通常異步數(shù)據(jù)檢索看起來可能像下例,我們使用 :id 參數(shù),嘗試加載一個(gè)用戶:

復(fù)制代碼 代碼如下:

app.get('/user/:id', function(req, res, next){
  loadUser(req.params.id, function(err, user){
    if (err) return next(err);
    res.send('Viewing user ' + user.name);
  });
});

為保證 DRY 原則和提升可讀,我們可以把這個(gè)邏輯應(yīng)用于一個(gè)中間件內(nèi)。如下所示,抽象這個(gè)邏輯到中間件內(nèi)將允許你重用它,同時(shí)保證了我們路由的簡(jiǎn)潔。
復(fù)制代碼 代碼如下:

function loadUser(req, res, next) {
  // You would fetch your user from the db
  var user = users[req.params.id];
  if (user) {
    req.user = user;
    next();
  } else {
    next(new Error('Failed to load user ' + req.params.id));
  }
}

app.get('/user/:id', loadUser, function(req, res){
  res.send('Viewing user ' + req.user.name);
});


多重路由可以,并按順序應(yīng)用到更深一層的邏輯,如限制一個(gè)用戶賬號(hào)的訪問。下面的例子只允許通過鑒定的用戶才可以編輯他(她)的賬號(hào)。
復(fù)制代碼 代碼如下:

function andRestrictToSelf(req, res, next) {
  req.authenticatedUser.id == req.user.id
    ? next()
    : next(new Error('Unauthorized'));
}

app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
  res.send('Editing user ' + req.user.name);
});


時(shí)刻銘記路由只是簡(jiǎn)單的函數(shù),如下所示,我們可以定義返回中間件的函數(shù)以創(chuàng)建一個(gè)更具表現(xiàn)力,更靈活的方案。
復(fù)制代碼 代碼如下:

function andRestrictTo(role) {
  return function(req, res, next) {
    req.authenticatedUser.role == role
      ? next()
      : next(new Error('Unauthorized'));
  }
}

app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
  res.send('Deleted user ' + req.user.name);
});


常用的中間件“堆?!笨梢酝ㄟ^一個(gè)數(shù)組來傳遞(會(huì)被遞歸應(yīng)用),這些中間件可以混著、匹配到任何層次(which can be mixed and matched to any degree)。
復(fù)制代碼 代碼如下:

var a = [middleware1, middleware2]
  , b = [middleware3, middleware4]
  , all = [a, b];

app.get('/foo', a, function(){});
app.get('/bar', a, function(){});

app.get('/', a, middleware3, middleware4, function(){});
app.get('/', a, b, function(){});
app.get('/', all, function(){});


對(duì)于這個(gè)實(shí)例的完整代碼,請(qǐng)看 route middleware example 這個(gè)倉庫。

我們可能會(huì)有多次想要“跳過”剩余的路由中間件,繼續(xù)匹配后續(xù)的路由。做到這點(diǎn),我們只需調(diào)用 next() 時(shí)帶上 'route' 字符串 —— next('route')。如果沒有余下的路由匹配到請(qǐng)求的 URL,Express 將會(huì)返回 404 Not Found。

十一、HTTP 方法

至此已接觸了好幾次 app.get(),除此這外 Express 還提供了其他常見的 HTTP 動(dòng)作,如 app.post() 、app.del() 等等。

POST 用法的一個(gè)常用例子是提交一個(gè)表單。下面我們簡(jiǎn)單地在 html 中把表單的 method 屬性設(shè)置為 post,控制權(quán)將會(huì)指派給它下面所定義的路由。

復(fù)制代碼 代碼如下:

<form method="post" action="/">
     <input type="text" name="user[name]" />
     <input type="text" name="user[email]" />
     <input type="submit" value="Submit" />
</form>

默認(rèn)上 Express 并不知道如何處理這個(gè)請(qǐng)求的內(nèi)容,因此我們必須添加 bodyParser 中間件,它將解析 application/x-www-form-urlencoded 和 application/json 請(qǐng)求的內(nèi)容,并把變量存放于 req.body 中。我們可以像下述示例一樣來使用這個(gè)中間件:
復(fù)制代碼 代碼如下:

app.use(express.bodyParser());

如下,我們的路由將有權(quán)訪問 req.body.user 對(duì)象,當(dāng)有 name 和 email 被定義時(shí)它將包含這兩個(gè)屬性(譯注:如果表單發(fā)送的內(nèi)容不為空的話)。
復(fù)制代碼 代碼如下:

app.post('/', function(req, res){
  console.log(req.body.user);
  res.redirect('back');
});

當(dāng)想在一個(gè)表單中使用像 PUT 這樣的方法,我們可以使用一個(gè)命名為 _method 的 hidden input,它可以用以修改 HTTP 方法。為了做這個(gè),我們首先需要 methodOverride 中間件,它必須出現(xiàn)于 bodyParser 后面,以便使用它的 req.body中所包含的表單值。
復(fù)制代碼 代碼如下:

app.use(express.bodyParser());
app.use(express.methodOverride());

對(duì)于這些方法為何不是默認(rèn)擁有,簡(jiǎn)單來說只是因?yàn)樗⒉皇?Express 所要求完整功能所必須。方法的使用依賴于你的應(yīng)用,你可能并不需要它們,客戶端依然能使用像 PUT 和 DELETE 這樣的方法,你可以直接使用它們,因?yàn)?methodOverride 為 form 提供了一個(gè)非常不錯(cuò)的解決方案。下面將示范如何使用 PUT 這個(gè)方法,看起來可能像:
復(fù)制代碼 代碼如下:

<form method="post" action="/">
  <input type="hidden" name="_method" value="put" />
  <input type="text" name="user[name]" />
  <input type="text" name="user[email]" />
  <input type="submit" value="Submit" />   
</form>

app.put('/', function(){
    console.log(req.body.user);
    res.redirect('back');
});

十二、錯(cuò)誤處理

Express 提供了 app.error() 方法以便接收到的異常在一個(gè)路由里拋出,或者傳到 next(err) 中。下面這個(gè)例子將基于特定的 NotFound 異常處理不同的頁面:

復(fù)制代碼 代碼如下:

function NotFound(msg){
  this.name = 'NotFound';
  Error.call(this, msg);
  Error.captureStackTrace(this, arguments.callee);
}

NotFound.prototype.__proto__ = Error.prototype;

app.get('/404', function(req, res){
  throw new NotFound;
});

app.get('/500', function(req, res){
  throw new Error('keyboard cat!');
});


如下述,我們可以多次調(diào)用 app.error()。這里我們檢測(cè) NotFound 的實(shí)例,并顯示 404 頁面,或者傳到 next 錯(cuò)誤處理器。值得注意的是這些處理器可以在任何地方定義,因?yàn)樗麄儗?huì)在 listen() 的時(shí)候被放置于路由處理器下面。它允許在 configure() 塊內(nèi)有定義,以便我們能基于環(huán)境用不同的異常處理方式。
復(fù)制代碼 代碼如下:

app.error(function(err, req, res, next){
    if (err instanceof NotFound) {
        res.render('404.jade');
    } else {
        next(err);
    }
});

為求簡(jiǎn)潔(for the simplicity),這里我們假定這個(gè) demo 的所有錯(cuò)誤為 500,當(dāng)然你可以可以選擇自己喜歡的。像 node 執(zhí)行文件系統(tǒng)的系統(tǒng)調(diào)用時(shí),你可能會(huì)接收到一個(gè)帶有 ENOENT 的 error.code,意思為 “不存在這樣的文件或目錄” 的錯(cuò)誤,我們可以在錯(cuò)誤處理器中使用,或者當(dāng)有需要時(shí)可顯示一個(gè)指定的頁面。
復(fù)制代碼 代碼如下:

app.error(function(err, req, res){
  res.render('500.jade', {
     error: err
  });
});

我們的 app 同樣可以利用 Connect 的 errorHandler 中間件來匯報(bào)異常。譬如當(dāng)我們希望在 “開發(fā)” 環(huán)境輸出 stderr 異常時(shí),我們可以使用:
復(fù)制代碼 代碼如下:

app.use(express.errorHandler({ dumpExceptions: true }));

同時(shí)在開發(fā)階段我們可能需要在花哨的 HTML 頁面顯示我們傳遞和拋出的異常,對(duì)此我們可以把 showStack 設(shè)置為 true。
復(fù)制代碼 代碼如下:

app.use(express.errorHandler({ showStack: true, dumpExceptions: true }));

errorHandler 中間件還可以在 Accept: application/json 存在的時(shí)候返回 json,這對(duì)于開發(fā)重度依賴客戶端 Javascript 的應(yīng)用非常有用。

十三、Route 參數(shù)預(yù)處理

路由參數(shù)預(yù)處理,通過隱式數(shù)據(jù)加載和請(qǐng)求驗(yàn)證,可以大大提升你程序的可讀性。打個(gè)比方,你通常需要持續(xù)地從多個(gè)路由獲取基本數(shù)據(jù)。像用 /user/:id 加載一個(gè)用戶,通常來說我們可能會(huì)這樣干:

復(fù)制代碼 代碼如下:

app.get('/user/:userId', function(req, res, next){
  User.get(req.params.userId, function(err, user){
    if (err) return next(err);
    res.send('user ' + user.name);
  });
});

通過預(yù)處理,我們的參數(shù)可以映射到執(zhí)行驗(yàn)證、控制(coercion),甚至從數(shù)據(jù)庫加載數(shù)據(jù)的回調(diào)。如下我們帶著參數(shù)名調(diào)用 app.param() 希望將其映射于某些中間件。如你所見,我們接受代表占位符值的 id 參數(shù)。使用這個(gè),我們?nèi)绯<虞d用戶并處理錯(cuò)誤,以及簡(jiǎn)單地調(diào)用 next() 來把控制權(quán)交由下一個(gè)預(yù)處理或者路由處理器。
復(fù)制代碼 代碼如下:

app.param('userId', function(req, res, next, id){
  User.get(id, function(err, user){
    if (err) return next(err);
    if (!user) return next(new Error('failed to find user'));
    req.user = user;
    next();
  });
});

一旦這樣做,上所述將會(huì)大大地提升路由的可讀性,并且允許我們輕松地在整個(gè)程序中共享邏輯:
復(fù)制代碼 代碼如下:

app.get('/user/:userId', function(req, res){
  res.send('user ' + req.user.name);
});


十四、View 處理

View 文件件使用 <name>.<engine> 這樣的格式,其中 <engine> 是被 require 進(jìn)來模塊的名。例如 layout.ejs 將告訴 view 系統(tǒng)去 require('ejs'),被加載的模塊必須(導(dǎo)出) exports.compile(str, options) 方法,并返回一個(gè) Function 來適應(yīng) Express。app.register() 可用以改變這種默認(rèn)行為,將文件擴(kuò)展名映射到特定的引擎。譬如 “foo.html” 可以由 ejs 來處理。

下面這個(gè)例子使用 Jade 來處理 index.html。因?yàn)槲覀儾⑽词褂?layout: false,index.jade 處理后的內(nèi)容將會(huì)被傳入到 layout.jade 中一個(gè)名為 body 的本地變量。

復(fù)制代碼 代碼如下:

app.get('/', function(req, res){
    res.render('index.jade', { title: 'My Site' });
});

新的 view engine 設(shè)置允許我們指定默認(rèn)的模板引擎,例如當(dāng)我們使用 jade 時(shí)可以這樣設(shè)置:
復(fù)制代碼 代碼如下:

app.set('view engine', 'jade');

允許我們這樣處理:
復(fù)制代碼 代碼如下:

res.render('index');

對(duì)應(yīng)于:
復(fù)制代碼 代碼如下:

res.render('index.jade');

當(dāng) view engine 被設(shè)定,擴(kuò)展名實(shí)屬可選,但我們依然可以混著匹配模板引擎:
復(fù)制代碼 代碼如下:

res.render('another-page.ejs');

Express 同時(shí)還提供了 view options 設(shè)置,這將應(yīng)用于一個(gè) view 每次被渲染的時(shí)候,譬如你不希望使用 layouts 的時(shí)候可能會(huì)這樣做:
復(fù)制代碼 代碼如下:

app.set('view options', {
    layout: false
});

在需要的時(shí)候,這可以在 res.render() 調(diào)用的內(nèi)部進(jìn)行重載:
復(fù)制代碼 代碼如下:

res.render('myview.ejs', { layout: true });

當(dāng)有需要變更一個(gè) layout,我們通常需要再指定一個(gè)路徑。譬如當(dāng)我們已經(jīng)把 view engine 設(shè)置為 jade,并且這個(gè)文件命名為 ./views/mylayout.jade,我們可以這樣簡(jiǎn)單地進(jìn)行傳參:
復(fù)制代碼 代碼如下:

res.render('page', { layout: 'mylayout' });

否則(譯注:沒有把 view engine 設(shè)置為 jade 或者其他的引擎時(shí)),我們必須指定一個(gè)擴(kuò)展名:
復(fù)制代碼 代碼如下:

res.render('page', { layout: 'mylayout.jade' });

它們同樣可以是絕對(duì)路徑:
復(fù)制代碼 代碼如下:

res.render('page', { layout: __dirname + '/../../mylayout.jade' });

對(duì)于這點(diǎn)有一個(gè)不錯(cuò)的例子 —— 自定義 ejs 的起始和閉合標(biāo)簽:
復(fù)制代碼 代碼如下:

app.set('view options', {
    open: '{{',
    close: '}}'
})

十五、View 部件

Express 的 view 系統(tǒng)內(nèi)置了部件(partials) 和集合器(collections)的支持,相當(dāng)于用一個(gè) “迷你” 的 view 替換一個(gè)文檔碎片(document fragment)。示例,在一個(gè) view 中重復(fù)渲染來顯示評(píng)論,我們可以使用部件集:

復(fù)制代碼 代碼如下:

partial('comment', { collection: comments });

如果并不需要其他選項(xiàng)或者本地變量,我們可以省略整個(gè)對(duì)象,簡(jiǎn)單地傳進(jìn)一個(gè)數(shù)組,這與上述是等價(jià)的:
復(fù)制代碼 代碼如下:

partial('comment', comments);

在使用中,部件集無償?shù)靥峁┝艘恍?“神奇” 本地變量的支持:

1.firstInCollection true,當(dāng)它是第一個(gè)對(duì)象的時(shí)候
2.indexInCollection 在集合器對(duì)象中的索引
3.lastInCollection true,當(dāng)它是最后一個(gè)對(duì)象的時(shí)候
4.collectionLength 集合器對(duì)象的長(zhǎng)度

本地變量的傳遞(生成)具備更高的優(yōu)先級(jí),同時(shí),傳到父級(jí) view 的本地變量對(duì)于子級(jí) view 同樣適應(yīng)。例如當(dāng)我們用 partial('blog/post', post) 來渲染一個(gè)博客文章,它將會(huì)生成一個(gè) post 本地變量,在調(diào)用這個(gè)函數(shù)的 view 中存在本地變量 user,它將同樣對(duì) blog/post 有效。(譯注:這里 partial 比較像 php 中的 include 方法)。

注意: 請(qǐng)謹(jǐn)慎使用部件集合器,渲染一個(gè)長(zhǎng)度為 100 的部件集合數(shù)組相當(dāng)于我們需要處理 100 個(gè) view。對(duì)于簡(jiǎn)單的集合,最好重復(fù)內(nèi)置,而非使用部件集合器以避免開銷過大。

十六、View 查找

View 查找相對(duì)于父級(jí) view (路徑)執(zhí)行,如我們有一個(gè) view 頁面叫作 views/user/list.jade,并且在其內(nèi)部寫有 partial('edit') 則它會(huì)嘗試加載 views/user/edit.jade,同理 partial('../messages') 將會(huì)加載 views/messages.jade。

View 系統(tǒng)還支持模板索引,允許你使用一個(gè)與 view 同名的目錄。例如在一個(gè)路由中,res.render('users') 得到的非 views/users.jade 即 views/users/index.jade。(譯注:先處理 <path>.<engine> 的情況,再處理 <path>/<index.<engine> 的情況,詳情可見 view.js。)

當(dāng)使用上述 view 索引,我們?cè)谂c view 同一個(gè)目錄下,使用 partial('users') 中引用 views/users/index.jade,與此同時(shí) view 系統(tǒng)會(huì)嘗試索引 ../users/index,而無須我們調(diào)用 partial('users')。

十七、Template Engines

下列為 Express 最常用的模板引擎:

1.Haml:haml 實(shí)現(xiàn)
2.Jade:haml.js 繼位者
3.EJS:嵌入式 JavaScript
4.CoffeeKup:基于 CoffeeScript 的模板
5.jQuery Templates

十八、Session 支持

Session 支持可以通過使用 Connect 的 session 中間件來獲得,為此通常我們同時(shí)需要在其前加上 cookieParser 中間件,它將解析和存儲(chǔ) cookie 數(shù)據(jù)于 req.cookies 中。

復(fù)制代碼 代碼如下:

app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat" }));

默認(rèn)情況下 session 中間件使用 Connect 內(nèi)置的內(nèi)存存儲(chǔ),然而還有其他多種實(shí)現(xiàn)方式。如 connect-redis 提供了一種 Redis 的 session 存儲(chǔ),它這可像下面這樣被使用:
復(fù)制代碼 代碼如下:

var RedisStore = require('connect-redis')(express);
app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));

至此,req.session 和 req.sessionStore 屬性將可以被所有路由和后繼的中間件使用。在 req.session 上的所有屬性都會(huì)在一個(gè)響應(yīng)中被自動(dòng)保存下來,譬如當(dāng)我們想要添加數(shù)據(jù)到購(gòu)物車:
復(fù)制代碼 代碼如下:

var RedisStore = require('connect-redis')(express);
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));

app.post('/add-to-cart', function(req, res){
  // 我們可能通過一個(gè)表單 POST 出多個(gè) item
  // (在些使用 bodyParser() 中間件)
  var items = req.body.items;
  req.session.items = items;
  res.redirect('back');
});

app.get('/add-to-cart', function(req, res){
  // 當(dāng)返回時(shí),頁面 GET /add-to-cart
  // 我們可以檢查 req.session.items && req.session.items.length
  // 來打印出提示
  if (req.session.items && req.session.items.length) {
    req.notify('info', 'You have %s items in your cart', req.session.items.length);
  }
  res.render('shopping-cart');
});


對(duì)于 req.session 對(duì)旬,它還有像 Session#touch()、Session#destroy()、 Session#regenerate() 等用以維護(hù)和操作 session 的方法。更多的詳情請(qǐng)看 Connect Session 的文檔。

十九、升級(jí)指南

對(duì)于使用 Express 1.x 的同學(xué),如果你有很重要的程序需要升級(jí)到 2.x 以獲得更好的支持,請(qǐng)看官方非常詳細(xì)的遷移指南http://expressjs.com/guide.html#migration-guide

相關(guān)文章

  • js實(shí)現(xiàn)圖片點(diǎn)擊左右輪播

    js實(shí)現(xiàn)圖片點(diǎn)擊左右輪播

    本文給大家分享的是使用javascript實(shí)現(xiàn)的圖片左右輪播的代碼,已經(jīng)封裝完畢,使用方法非常簡(jiǎn)單,參考示例,有需要的小伙伴可以參考下。
    2015-07-07
  • 簡(jiǎn)潔短小的 JavaScript IE 瀏覽器判定代碼

    簡(jiǎn)潔短小的 JavaScript IE 瀏覽器判定代碼

    IE瀏覽器不管是什么版本,總是跟Web標(biāo)準(zhǔn)有些不太兼容。對(duì)于代碼工作者來說,自然是苦不堪言,為了考慮IE的兼容問題,不管是寫 CSS 還是 JS,往往都要對(duì) IE 特別對(duì)待,這就少不了做些判斷。本文不討論如何區(qū)分 IE 的樣式,僅是 JS 判定 IE 瀏覽器。
    2010-03-03
  • 什么是JavaScript中的結(jié)果值?

    什么是JavaScript中的結(jié)果值?

    你知道JavaScript中的結(jié)果值是什么嗎?這篇文章主要介紹了JavaScript結(jié)果值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • JavaScript中的閉包介紹

    JavaScript中的閉包介紹

    這篇文章主要介紹了JavaScript中的閉包介紹,本文講解了Javacript 閉包、Javscript 閉包與this、Javscript 閉包與讀寫變量等內(nèi)容,需要的朋友可以參考下
    2015-03-03
  • uni-app配置APP自定義頂部標(biāo)題欄設(shè)置方法與注意事項(xiàng)

    uni-app配置APP自定義頂部標(biāo)題欄設(shè)置方法與注意事項(xiàng)

    相信很多小伙伴在使用uniapp進(jìn)行多端開發(fā)的時(shí)候,在面對(duì)一些業(yè)務(wù)需求的時(shí)候,uniapp給我們提供的默認(rèn)導(dǎo)航欄已經(jīng)不能滿足我們的業(yè)務(wù)需求了,這篇文章主要給大家介紹了關(guān)于uni-app配置APP自定義頂部標(biāo)題欄設(shè)置方法與注意事項(xiàng)的相關(guān)資料,需要的朋友可以參考下
    2022-07-07
  • 微信小程序 數(shù)據(jù)緩存實(shí)現(xiàn)方法詳解

    微信小程序 數(shù)據(jù)緩存實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了微信小程序 數(shù)據(jù)緩存實(shí)現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • javascript日期計(jì)算實(shí)例分析

    javascript日期計(jì)算實(shí)例分析

    這篇文章主要介紹了javascript日期計(jì)算,涉及javascript針對(duì)日期計(jì)算的相關(guān)技巧,這里需要注意索引的使用,需要的朋友可以參考下
    2015-06-06
  • js拼接字符串時(shí)如何在中間加上空格

    js拼接字符串時(shí)如何在中間加上空格

    這篇文章主要介紹了js拼接字符串時(shí)如何在中間加上空格的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 微信小程序頁面跳轉(zhuǎn)功能之從列表的item項(xiàng)跳轉(zhuǎn)到下一個(gè)頁面的方法

    微信小程序頁面跳轉(zhuǎn)功能之從列表的item項(xiàng)跳轉(zhuǎn)到下一個(gè)頁面的方法

    這篇文章主要介紹了微信小程序頁面跳轉(zhuǎn)功能之從列表的item項(xiàng)跳轉(zhuǎn)到下一個(gè)頁面的方法,結(jié)合具體實(shí)例形式總結(jié)分析了微信小程序頁面跳轉(zhuǎn)及列表item項(xiàng)跳轉(zhuǎn)頁面的相關(guān)操作技巧,需要的朋友可以參考下
    2017-11-11
  • 原生js滑動(dòng)輪播封裝

    原生js滑動(dòng)輪播封裝

    這篇文章主要為大家詳細(xì)介紹了原生js滑動(dòng)輪播封裝,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07

最新評(píng)論