前面的內容大都是關於 Express 框架自身的內容,包括:Express 簡介、工做原理、框架特色。在系列的最後,咱們將把注意力放到 Express 框架周邊工具鏈上。學習若是使用這些工具來拓展 Express 框架的功能。css
首先,本文咱們將會討論視圖模版引擎的使用。經過這些模版引擎咱們能過動態的生成 HTML 內容。在前面咱們已經使用過 EJS 並使用變量語法實現內容的注入。可是這些內容只是整個模版引擎部分的冰山一角。接下來,將會學習到多種內容注入方式、EJS,Pug 等模版引擎的特性。html
<!--more-->git
在開始以前,咱們有必要解釋下何爲視圖引擎(view engine)?視圖引擎做爲編程術語它主要意思是指「進行視圖渲染的模塊」。而 Express 框架中最經常使用的兩個視圖引擎是 Pug 和 EJS 。須要注意的是,Pug 早期的名稱是 Jade 因爲某些緣由不得已更名 。github
另外,Express 並無指定必須使用何種引擎。只要該視圖引擎的設計符合 Express API 規範,你就能夠將其應用到工程中。下面,咱們看看它究竟是如何工做的。web
下面咱們經過一個簡單示例回顧下 EJS 渲染過程:express
var express = require("express"); var path = require("path"); var app = express(); app.set("view engine", "ejs"); app.set("views", path.resolve(__dirname, "views")); app.get("/", function(req, res) { res.render("index"); }); app.listen(3000);
在運行代碼以前,你須要經過 npm install 安裝 EJS 和 Express。在安裝完成後訪問應用主頁的話,程序就會尋找 views/index.ejs 文件並使用 EJS 對其進行渲染。另外,工程中通常都只會使用一個視圖引擎,由於多個引擎會給工程引入沒必要要的複雜性。npm
下面咱們看一個更爲複雜的示例,其中會同時用到兩個視圖引擎 Pug 和 EJS:編程
var express = require("express"); var path = require("path"); var ejs = require("ejs"); var app = express(); app.locals.appName = "Song Lyrics"; app.set("view engine", "jade"); app.set("views", path.resolve(__dirname, "views")); app.engine("html", ejs.renderFile); app.use(function(req, res, next) { res.locals.userAgent = req.headers["user-agent"]; next(); }); app.get("/about", function(req, res) { res.render("about", { currentUser: "india-arie123" }); }); app.get("/contact", function(req, res) { res.render("contact.ejs"); }); app.use(function(req, res) { res.status(404); res.render("404.html", { urlAttempted: req.url }); }); app.listen(3000);
雖然代碼看起來比較複雜,但其實分解後步驟也還簡單。下面,咱們就對上面調用 render 處的代碼進行分析:json
Express 首先會將全部請求都公用的 app.local 中已存在的屬性添加視圖中。而後添加 res.locals 中的屬性並對可能與 app.local 衝突的屬性進行覆蓋操做。最後,添加 render 調用處的屬性而且也可能進行覆蓋操做。例如,訪問 /about 路徑時,上下文對象就包含三個屬性:appname、userAgent、currentUser;訪問 /contact 路徑時,上下文對象的屬性就只有 appname、userAgent ;而進行 404 處理時上下文對象的屬性就變成了:appname、userAgent、urlAttempted 。api
緊接着,咱們將會設置是否啓用視圖緩存。其實視圖緩存並非緩存視圖實際上它緩存的視圖路徑。例如,它會將 views/my_views.ejs 路徑緩存起來並綁定到 EJS 引擎上。
Express 經過兩種方式來決定是否對視圖文件進行緩存: 文檔記錄方式:經過調用 *app.enabled("view cache")* 開啓。在開發模式下默認是被禁用的,可是你能夠在正式生產環境中開啓。固然,你能夠經過 *app.disable("view cache")* 手動關閉。 非文檔記錄方式:根據第一步上下文中的 *cache* 對象是否爲 *true* 來決定是否緩存該文件。這樣你就能夠對每個文件進行自定義設置了。
同時使用多個視圖引擎確實爲程序增長了沒必要要的複雜性,好在絕大多數時候咱們並不會這樣作。
Express 給客戶端默認響應的內容是 HTML。雖然大多數時候這沒什麼問題,可是有時可能須要返回的是純文本、XML、JSON 等格式。此時,你能夠經過修改參數 res.type 進行自定義設置:
app.get(「/」, function(req, res) { res.type(「text」); res.render(「myview」, { currentUser: 「Gilligan」 }); }
固然,你可使用更簡單的 res.json
除了 EJS 和 Pug 以外,其實還有不少中模版引擎。可是這些模版引擎可能並不像 EJS 和 Pug 那樣是爲 Express 專門設計的。此時若是須要使用這些未適配的模版引擎,咱們就不得不使用 Consolidate.js 進行封裝以期可以兼容 Express API。並且 Consolidate.js 支持的種類也很是多,你能夠在項目首頁看到完整的支持列表。
假設,如今你正是使用的引擎是與 Express 並不兼容的 Walrus。那麼,下面咱們看 Consolidate 是如何進行兼容適配工做的。
首先,使用 npm install walrus consolidate 安裝相關類庫和依賴項,而後咱們將其引入:
var express = require("express"); var engines = require("consolidate"); var path = require("path"); var app = express(); app.set("view engine", "wal"); app.engine("wal", engines.walrus); app.set("views", path.resolve(__dirname, "views")); app.get("/", function(req, res) { res.render("index"); }); app.listen(3000);
是否是很簡單?只需幾行代碼咱們就完成了整個適配工做。因此當有兼容適配需求的時候,我強烈建議你使用 Consolidate 而不是本身悶頭幹。
EJS 是 Express 中最簡單也是最受歡迎的視圖引擎之一。它能夠爲字符串、HTML、純文本建立模版,並且他的集成也很是簡單。它在瀏覽器和 Node 環境中都能正常工做。它與 Ruby 中的 ERB 與法很是的相似。
實際上存在由不一樣組織維護的兩個不一樣版本的 EJS。雖然在功能上它們很類似,可是並非同一個類庫。其中 Express 中使用的 EJS 是由 TJ Holowaychuck 維護的,你能夠經過 npm 查找到該類庫。另外一個同名類庫在 09 年就中止了更新且它不能在 Node 環境中運行。
除了用作 HTML 模版以外,它還能應用於字符串和純文本中。請看 EJS 是如何對下面文本模版進行渲染的:
Hi <%= name %>! You were born in <%= birthyear %>, so that means you're <%= (new Date()).getFullYear() - birthyear %> years old. <% if (career) { -%> <%=: career | capitalize %> is a cool career! <% } else { -%> Haven't started a career yet? That's cool. <% } -%> Oh, let's read your bio: <%- bio %> See you later!
將下面的 JSON 數據傳入上面摸板中:
{ name: "Tony Hawk", birthyear: 1968, career: "skateboarding", bio: "<b>Tony Hawk</b> is the coolest skateboarder around." }
最終,獲得的渲染結果是(假設當前是 2015 年):
Hi Tony Hawk! You were born in 1968, so that means you’re 47 years old. Skateboarding is a cool career! Oh, let’s read your bio: Tony Hawk is the coolest skateboarder around. See you later!
該示例演示了 EJS 經常使用的四種語法:打印、打印並轉義、執行 JS 代碼、過濾。
在 EJS 你可使用兩種語法打印表達式的值:<%= expression %> 和 <%- expression %>,其中前者會對結果進行 HTML 轉義。例如,當傳入的 expression 值爲 Express 時,前者執行的結果是 Express 然後者獲得的字符串是 Express。我建議你使用前一種方式,由於它更爲可靠。
一樣,EJS 還容許你 經過 <% expression %> 語法在其中執行 JS 表達式,而且該表達式並不會被打印出來。該特性在執行循環和條件判斷的時候很是有用。另外,你還能夠經過 <% expression -%> 避免沒必要要的換行。
經過 <%=: expression | xxx %> 語法,咱們能夠對錶達式結果再進行一次過濾處理。例如,上面咱們就對錶達式結果應用了首字母大寫過濾器。固然,除了自帶的大量過濾器以外,你還能夠進行自定義。
這裏,我作了一個 EJS 的示例程序。雖然界面不是很好看,可是你能從中熟悉 EJS 的各類語法使用。
EJS 引擎容許你在當前模版中使用另外一個 EJS 模版。這樣咱們就能對整個進行組件拆分複用。例如,將 HTML 的頭部和尾部拆分爲 header 和 footer 模塊,而後在其餘模版中進行組合複用。
示例以下:首先咱們建立 header.ejs 並拷貝代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="/the.css"> <title><%= appTitle %>/title> </head> <body> <header> <h1><%= appTitle %></h1> </header>
緊接着建立 footer 組件 footer.ejs 並拷貝代碼:
<footer> All content copyright <%= new Date().getFullYear() %> <%= appName %>. </footer> </body> </html>
最後,咱們經過 include 語法進行組件嵌入操做:
<% include header %> <h1>Welcome to my page!</h1> <p>This is a pretty cool page, I must say.</p> <% include footer %>
假設,你如今須要實現一個展現用戶信息的組建,那麼你能夠建立 userwidget.ejs 文件並拷貝:
<div class="user-widget"> <img src="<%= user.profilePicture %>"> <div class="user-name"><%= user.name %></div> <div class="user-bio"><%= user.bio %></div> </div>
那麼,在渲染當前用戶時能夠這樣使用該模版:
<% user = currentUser %> <% include userwidget %>
或者在渲染用戶列表時:
<% userList.forEach(function(user) { %> <% include userwidget %> <% } %>
經過 EJS 中的 include 語法,咱們能夠在建立模版的同時將其做爲組件進行子視圖的渲染操做。
Express 內置的 22 個過濾器,其中包括對數組和字符串的經常使用操做。一般狀況下,它們能過知足你的需求,可是有時你不得不添加本身的過濾器。
假設,如今你已經引入了 EJS 模塊並將其保存到名爲 ejs 變量中。那麼你能夠爲按照下面的方式爲 ejs.filters 拓展一個用於數組求和的過濾器。
ejs.filters.sum = function(arr) { var result = 0; for (var i = 0; i < arr.length; i++) { result += arr[i]; } return result; };
而後,你就能夠在代碼中使用該過濾器了:
<%=: myarray | sum %>
實現和使用都很是簡單,因此我建議你將那些經常使用操做實現爲過濾器。
像 Handlebars ,Mustache ,以及 EJS 這樣的視圖引擎只是在 HTML 拓展了新語法它並無對 HTML 語法形成破壞。對於一個瞭解 HTML 語法的設計師來講這最好不過了,畢竟不用學習新語言。一樣它們還適用於非 HTML 模版環境,而這一點則是 Pug 的軟肋。
可是 Pug 也有本身獨特的優點。它能減小你的代碼量,並且代碼風格也很是不錯。尤爲在寫 HTML 模版時,標籤會嵌套縮進並且無需閉合。另外,EJS 風格的判斷和循環語法也是內置的。雖然須要學的東西比較多,可是它的功能也異常強大。
像 HTML 這樣的語言是嵌套的,其中有根元素(<html>)以及各類子元素(像 <head> 和 <body> ),而子元素還能夠進一步嵌套其餘元素。另外,HTML 的元素必須像 XML 同樣須要閉合。
而 Pug 則採用了不一樣的縮進語法。下面的代碼就展現了使用 Pug 實現的簡單 web 頁面:
doctype html html(lang="en") head title Hello world! body h1 This is a Pug example #container p Wow.
上面的代碼中內容將被轉變爲下面的 HTML。
<!DOCTYPE html> <html lang="en"> <head> <title>Hello world!</title> </head <body> <h1>This is a Pug example</h1> <div id="container"> <p>Wow.</p> </div> </body> </html>
你能夠去 Pug 項目主頁去查看它時如何實現這種轉變的。
佈局時全部模版語言的一個重要特性。它可讓咱們實現公共組件而後在其餘文件中實現複用。例如,咱們能夠將頁面的 header 和 footer 抽離出來。這樣不只能夠保證全部頁面的 header 和 footer 內容的一致,並且修改起來也更加方便。
Pug 佈局的實現步驟大體以下:
第一步,爲全部頁面定義一個主佈局文件,而該文件幾乎就是一個空模版。它用 block 語法進行佔位操做,而後實際生成的頁面會使用內容替換這些佔位符。示例以下:
doctype html html head meta(charset="utf-8") title Cute Animals website link(rel="stylesheet" href="the.css") block header body h1 Cute Animals website block body
你能夠看到上面定義了 header 和 body 兩個佔位符。下面咱們將它保存到 layout.jade 文件中。緊接着咱們實現其中的 body 塊:
extends layout.jade block body p Welcome to my cute animals page!
layout.jade 將會被渲染成:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Cute Animals website</title> <link rel="stylesheet" href="the.css"> </head> <body> <h1>Cute Animals website</h1> <p>Welcome to my cute animals page!</p> </body> </html>
注意到當你拓展主佈局時,並不必定須要實現其中的全部佔位塊。例如上面就沒有實現 header 。
在其餘頁面能夠對 body 塊進行不一樣的實現:
extends layout.jade block body p This is another page using ths layout. img(src="cute_dog.jpg" alt="A cute dog!") p Isn't that a cute dog!
Pug 經過佈局進行組件分離讓咱們能夠避免一些重複的代碼。
Pug 中還有一個被稱爲 Mixins 的酷炫特性。經過該特性你能夠對文件中可能須要反覆使用的功能進行一次定義。下面,咱們就經過該特性對前面 EJS 部分用戶信息展現的功能進行從新實現:
mixin user-widget(user) .user-widget img(src=user.profilePicture) .user-name= user.name .user-bio= user.bio // 展現當前用戶 +user-widget(currentUser) // 展現用戶列表 - each user in userList +user-widget(user)
Pug 的基礎內容到此爲止,更多語法細節請查看官方文檔。
這章的內容包括:
原文地址