资讯专栏INFORMATION COLUMN

Express 实战(七):视图与模板:Pug 和 EJS

wmui / 893人阅读

摘要:而框架中最常用的两个视图引擎是和。实际上这些上下文对象就是会在视图中使用到的变量。其实视图缓存并不是缓存视图实际上它缓存的视图路径。根据默认视图引擎将缺少拓展名的视图文件补充完整。实际上存在由不同组织维护的两个不同版本的。


前面的内容大都是关于 Express 框架自身的内容,包括:Express 简介、工作原理、框架特点。在系列的最后,我们将把注意力放到 Express 框架周边工具链上。学习如果使用这些工具来拓展 Express 框架的功能。

首先,本文我们将会讨论视图模版引擎的使用。通过这些模版引擎我们能过动态的生成 HTML 内容。在前面我们已经使用过 EJS 并使用变量语法实现内容的注入。但是这些内容只是整个模版引擎部分的冰山一角。接下来,将会学习到多种内容注入方式、EJS,Pug 等模版引擎的特性。

Express 视图特性

在开始之前,我们有必要解释下何为视图引擎(view engine)?视图引擎作为编程术语它主要意思是指“进行视图渲染的模块”。而 Express 框架中最常用的两个视图引擎是 Pug 和 EJS 。需要注意的是,Pug 早期的名称是 Jade 由于某些原因不得已改名 。

另外,Express 并没有指定必须使用何种引擎。只要该视图引擎的设计符合 Express API 规范,你就可以将其应用到工程中。下面,我们看看它到底是如何工作的。

简单的视图渲染示例

下面我们通过一个简单示例回顾下 EJS 渲染过程:

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 对其进行渲染。另外,工程中一般都只会使用一个视图引擎,因为多个引擎会给工程引入不必要的复杂性。

复杂的视图渲染

下面我们看一个更为复杂的示例,其中会同时用到两个视图引擎 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 处的代码进行分析:

Express 在你每次调用 render 时都会创建上下文对象,并且在进行渲染时会传入到视图引擎中。实际上这些上下文对象就是会在视图中使用到的变量。

Express 首先会将所有请求都公用的 app.local 中已存在的属性添加视图中。然后添加 res.locals 中的属性并对可能与 app.local 冲突的属性进行覆盖操作。最后,添加 render 调用处的属性并且也可能进行覆盖操作。例如,访问 /about 路径时,上下文对象就包含三个属性:appnameuserAgentcurrentUser;访问 /contact 路径时,上下文对象的属性就只有 appnameuserAgent ;而进行 404 处理时上下文对象的属性就变成了:appnameuserAgenturlAttempted

紧接着,我们将会设置是否启用视图缓存。其实视图缓存并不是缓存视图实际上它缓存的视图路径。例如,它会将 views/my_views.ejs 路径缓存起来并绑定到 EJS 引擎上。

Express 通过两种方式来决定是否对视图文件进行缓存:

文档记录方式:通过调用 *app.enabled("view cache")* 开启。在开发模式下默认是被禁用的,但是你可以在正式生产环境中开启。当然,你可以通过 *app.disable("view cache")* 手动关闭。

非文档记录方式:根据第一步上下文中的 *cache* 对象是否为 *true* 来决定是否缓存该文件。这样你就可以对每一个文件进行自定义设置了。

接下来,Express 会设置视图文件名及其使用的视图引擎。如果在第二步中已经进行了视图缓存则可以直接跳到最后一步。否则,则继续下一步。

根据默认视图引擎将缺少拓展名的视图文件补充完整。在本例中,about 会被拓展成 about.jade ,而 contact.ejs 以及 404.html 文件会保存不变。如果你既没有指定默认视图引擎也没有明确拓展名,那么程序会出现崩溃。

通过文件拓展名进行视图引擎匹配。对于 .html 格式文件则根据 app.engine("html", xx); 设置进行匹配。

在视图问价夹下,查找视图文件名对应的文件。如果不存在则报错。

判断查找到的视图文件是否需要进行缓存。

使用引擎对视图文件进行渲染并生成最终的 HTML 文件。

同时使用多个视图引擎确实为程序增加了不必要的复杂性,好在绝大多数时候我们并不会这样做。

Express 给客户端默认响应的内容是 HTML。虽然大多数时候这没什么问题,但是有时可能需要返回的是纯文本、XML、JSON 等格式。此时,你可以通过修改参数 res.type 进行自定义设置:

app.get(“/”, function(req, res) { 
  res.type(“text”); 
  res.render(“myview”, { 
   currentUser: “Gilligan” 
  }); 
} 

当然,你可以使用更简单的 res.json

视图引擎的 Express 兼容设置:Consolidate.js

除了 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 中你必须要了解的东西

EJS 是 Express 中最简单也是最受欢迎的视图引擎之一。它可以为字符串、HTML、纯文本创建模版,而且他的集成也非常简单。它在浏览器和 Node 环境中都能正常工作。它与 Ruby 中的 ERB 与法非常的类似。

实际上存在由不同组织维护的两个不同版本的 EJS。虽然在功能上它们很相似,但是并不是同一个类库。其中 Express 中使用的 EJS 是由 TJ Holowaychuck 维护的,你可以通过 npm 查找到该类库。另一个同名类库在 09 年就停止了更新且它不能在 Node 环境中运行。

EJS 语法

除了用做 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: "Tony Hawk 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 模版

EJS 引擎允许你在当前模版中使用另一个 EJS 模版。这样我们就能对整个进行组件拆分复用。例如,将 HTML 的头部和尾部拆分为 headerfooter 模块,然后在其他模版中进行组合复用。

示例如下:首先我们创建 header.ejs 并拷贝代码:




    
    
    <%= appTitle %>/title>
</head>
<body>
    <header>
        <h1><%= appTitle %></h1>
    </header></pre>
<p>紧接着创建 footer 组件 <em>footer.ejs</em> 并拷贝代码:</p>
<pre><footer>
    All content copyright <%= new Date().getFullYear() %> <%= appName %>.
</footer>
</body>
</html></pre>
<p>最后,我们通过 <em>include</em> 语法进行组件嵌入操作:</p>
<pre><% include header %>
    <h1>Welcome to my page!</h1>
    <p>This is a pretty cool page, I must say.</p>
<% include footer %></pre>
<p>假设,你现在需要实现一个展示用户信息的组建,那么你可以创建 <em>userwidget.ejs</em> 文件并拷贝:</p>
<pre><div class="user-widget">
    <img src="<%= user.profilePicture %>">
    <div class="user-name"><%= user.name %></div>
    <div class="user-bio"><%= user.bio %></div>
</div></pre>
<p>那么,在渲染当前用户时可以这样使用该模版:</p>
<pre><% user = currentUser %>
<% include userwidget %></pre>
<p>或者在渲染用户列表时:</p>
<pre><% userList.forEach(function(user) { %>
  <% include userwidget %>
<% } %></pre>
<p>通过 EJS 中的 <em>include</em> 语法,我们可以在创建模版的同时将其作为组件进行子视图的渲染操作。</p>
<b>添加你自己的过滤器</b>
<p>Express 内置的 22 个过滤器,其中包括对数组和字符串的常用操作。通常情况下,它们能过满足你的需求,但是有时你不得不添加自己的过滤器。</p>
<p>假设,现在你已经引入了 EJS 模块并将其保存到名为 <em>ejs</em> 变量中。那么你可以为按照下面的方式为 <em>ejs.filters</em> 拓展一个用于数组求和的过滤器。</p>
<pre>ejs.filters.sum = function(arr) {
  var result = 0;
  for (var i = 0; i < arr.length; i++) {
    result += arr[i];
  }
  return result;
};</pre>
<p>然后,你就可以在代码中使用该过滤器了:</p>
<pre><%=: myarray | sum %></pre>
<p>实现和使用都非常简单,所以我建议你将那些常用操作实现为过滤器。</p>
<b>Pug 中你必须要了解的东西</b>
<p>像 Handlebars ,Mustache ,以及 EJS 这样的视图引擎只是在 HTML 拓展了新语法它并没有对 HTML 语法造成破坏。对于一个了解 HTML 语法的设计师来说这最好不过了,毕竟不用学习新语言。同样它们还适用于非 HTML 模版环境,而这一点则是 Pug 的软肋。</p>
<p>但是 Pug 也有自己独特的优势。它能减少你的代码量,而且代码风格也非常不错。尤其在写 HTML 模版时,标签会嵌套缩进而且无需闭合。另外,EJS 风格的判断和循环语法也是内置的。虽然需要学的东西比较多,但是它的功能也异常强大。</p>
<b>Pug 语法</b>
<p>像 HTML 这样的语言是嵌套的,其中有根元素(<html>)以及各种子元素(像 <head> 和 <body> ),而子元素还可以进一步嵌套其他元素。另外,HTML 的元素必须像 XML 一样需要闭合。</p>
<p>而 Pug 则采用了不同的缩进语法。下面的代码就展示了使用 Pug 实现的简单 web 页面:</p>
<pre>doctype html
html(lang="en") 
  head
    title Hello world!
  body
    h1 This is a Pug example
    #container 
      p Wow.</pre>
<p>上面的代码中内容将被转变为下面的 HTML。</p>
<pre><!DOCTYPE html>
<html lang="en">
<head>
    <title>Hello world!

        

This is a Pug example

Wow.

你可以去 Pug 项目主页去查看它时如何实现这种转变的。

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  

你可以看到上面定义了 headerbody 两个占位符。下面我们将它保存到 layout.jade 文件中。紧接着我们实现其中的 body 块:

extends layout.jade
block body
  p Welcome to my cute animals page!

layout.jade 将会被渲染成:



  
    
    Cute Animals website
    
  
  
    

Cute Animals website

Welcome to my cute animals page!

注意到当你拓展主布局时,并不一定需要实现其中的所有占位块。例如上面就没有实现 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 功能

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 的基础内容到此为止,更多语法细节请查看官方文档。

总结

这章的内容包括:

Express 的视图系统,以及它是如何进行动态渲染的。

EJS 引擎的语法和基本使用。

Pug 引擎的语法和基本使用。

原文地址

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84846.html

相关文章

  • Express 文档(使用模板引擎)

    摘要:一些与一起使用的流行模板引擎是和,应用程序生成器使用作为其默认值,但它也支持其他几个。有关可与一起使用的模板引擎列表,请参阅模板引擎,另请参阅比较模板引擎等。,使用的模板引擎,例如,要使用模板引擎。 使用模板引擎 模板引擎使你可以在应用程序中使用静态模板文件,在运行时,模板引擎用实际值替换模板文件中的变量,并将模板转换为发送到客户端的HTML文件,这种方法可以更轻松地设计HTML页面。...

    gxyz 评论0 收藏0
  • Express 实战(三):Express 基础

    摘要:同样的的框架中也有被称为中间件概念。所以,整个工作流有两种可能情形另外,这些中间件函数中部分函数需要对响应做出响应。扩展和在原来基础上对和对象进行了功能扩展。除了对响应对象进行了拓展之 Express 框架的初衷是为了拓展 Node 内置模块的功能提高开发效率。当你深入研究后就会发现,Express 其实是在 Node 内置的 HTTP 模块上构建了一层抽象。理论上所有 Express...

    KoreyLee 评论0 收藏0
  • 实战】用 express+MongoDB 搭建一个完整的前端项目

    摘要:前言要做一个全沾的工程师,对于后端和数据库来说,即使不认识也要见个面的。基本了解的概念就好,主要是安装上数据库,并进行简单的增删操作。 前言:要做一个全沾的工程师,对于后端和数据库来说,即使不认识也要见个面的。本文给的例子很简单,也贴出来源码,只要一步步下来,就可以跑起来啦~~~ 思考一个需求:做一个登录页面,自己搭建服务和数据库,将用户输入的登录信息保存到数据库如何完成呢:首先选择...

    Steve_Wang_ 评论0 收藏0
  • Express 文档(Express生成器)

    摘要:应用程序生成器使用应用程序生成器工具快速创建应用程序框架。生成的应用程序具有以下目录结构生成器创建的应用程序结构只是构建应用程序的众多方法之一,随意使用此结构或修改它以最好地满足你的需求。 Express应用程序生成器 使用应用程序生成器工具express-generator快速创建应用程序框架。 express-generator包安装了express命令行工具,使用以下命令执行此操...

    abson 评论0 收藏0
  • express web的一款mvc框架

    摘要:相关的网站开发有这么常用的四款数据层视图层控制层框架,我们今天说一下阿里的框架是一个自身功能极简,完全是由路由和中间件构成一个的开发框架安装创建一个目录通过命令为你的应用创建一个文件。修改请求和响应对象。注意只对使用或加载的中间件有效。 express express相关的网站*http://expressjs.com/*http://www.expressjs.com.cn/*htt...

    _ang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<