前面的内容大都是关于 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 的基础内容到此为止,更多语法细节请查看官方文档。
这章的内容包括:
原文地址