模板引擎(Template Engine)是一个将页面模板和数据结合起来生成 html 的工具。上例中,我们只是返回纯文本给浏览器,现在我们修改代码返回一个 html 页面给浏览器。

3.3.1 ejs

模板引擎有很多,ejs 是其中一种,因为它使用起来十分简单,而且与 express 集成良好,所以我们使用 ejs。安装 ejs:

  1. npm i ejs --save

修改 index.js 如下:

index.js

  1. const path = require('path')
  2. const express = require('express')
  3. const app = express()
  4. const indexRouter = require('./routes/index')
  5. const userRouter = require('./routes/users')
  6. app.set('views', path.join(__dirname, 'views'))// 设置存放模板文件的目录
  7. app.set('view engine', 'ejs')// 设置模板引擎为 ejs
  8. app.use('/', indexRouter)
  9. app.use('/users', userRouter)
  10. app.listen(3000)

通过 app.set 设置模板引擎为 ejs 和存放模板的目录。在 myblog 下新建 views 文件夹,在 views 下新建 users.ejs,添加如下代码:

views/users.ejs

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <style type="text/css">
  5. body {padding: 50px;font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
  6. </style>
  7. </head>
  8. <body>
  9. <h1><%= name.toUpperCase() %></h1>
  10. <p>hello, <%= name %></p>
  11. </body>
  12. </html>

修改 routes/users.js 如下:

routes/users.js

  1. const express = require('express')
  2. const router = express.Router()
  3. router.get('/:name', function (req, res) {
  4. res.render('users', {
  5. name: req.params.name
  6. })
  7. })
  8. module.exports = router

通过调用 res.render 函数渲染 ejs 模板,res.render 第一个参数是模板的名字,这里是 users 则会匹配 views/users.ejs,第二个参数是传给模板的数据,这里传入 name,则在 ejs 模板中可使用 name。res.render 的作用就是将模板和数据结合生成 html,同时设置响应头中的 Content-Type: text/html,告诉浏览器我返回的是 html,不是纯文本,要按 html 展示。现在我们访问 localhost:3000/users/haha,如下图所示:

模板引擎 - 图1

上面代码可以看到,我们在模板 <%= name.toUpperCase() %> 中使用了 JavaScript 的语法 .toUpperCase() 将名字转化为大写,那这个 <%= xxx %> 是什么东西呢?ejs 有 3 种常用标签:

  1. <% code %>:运行 JavaScript 代码,不输出
  2. <%= code %>:显示转义后的 HTML内容
  3. <%- code %>:显示原始 HTML 内容

注意:<%= code %><%- code %> 都可以是 JavaScript 表达式生成的字符串,当变量 code 为普通字符串时,两者没有区别。当 code 比如为 <h1>hello</h1> 这种字符串时,<%= code %> 会原样输出 <h1>hello</h1>,而 <%- code %> 则会显示 H1 大的 hello 字符串。

下面的例子解释了 <% code %> 的用法:

Data

  1. supplies: ['mop', 'broom', 'duster']

Template

  1. <ul>
  2. <% for(var i=0; i<supplies.length; i++) {%>
  3. <li><%= supplies[i] %></li>
  4. <% } %>
  5. </ul>

Result

  1. <ul>
  2. <li>mop</li>
  3. <li>broom</li>
  4. <li>duster</li>
  5. </ul>

更多 ejs 的标签请看 官方文档

3.3.2 includes

我们使用模板引擎通常不是一个页面对应一个模板,这样就失去了模板的优势,而是把模板拆成可复用的模板片段组合使用,如在 views 下新建 header.ejs 和 footer.ejs,并修改 users.ejs:

views/header.ejs

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <style type="text/css">
  5. body {padding: 50px;font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
  6. </style>
  7. </head>
  8. <body>

views/footer.ejs

  1. </body>
  2. </html>

views/users.ejs

  1. <%- include('header') %>
  2. <h1><%= name.toUpperCase() %></h1>
  3. <p>hello, <%= name %></p>
  4. <%- include('footer') %>

我们将原来的 users.ejs 拆成出了 header.ejs 和 footer.ejs,并在 users.ejs 通过 ejs 内置的 include 方法引入,从而实现了跟以前一个模板文件相同的功能。

小提示:拆分模板组件通常有两个好处:

  1. 模板可复用,减少重复代码
  2. 主模板结构清晰

注意:要用 <%- include('header') %> 而不是 <%= include('header') %>