Skip to content
On this page

Express模板引擎

模板引擎允许您在服务器端使用静态模板文件生成动态HTML页面。在运行时,模板引擎将模板文件中的变量替换为实际值,并将模板转换为HTML文件发送给客户端。

模板引擎概览

常用模板引擎

Express支持多种模板引擎,包括:

  • EJS (Embedded JavaScript templates) - 简单易用
  • Pug (formerly Jade) - 语法简洁
  • Handlebars - 逻辑较少的模板
  • Mustache - 逻辑无关的模板
  • Nunjucks - 功能丰富的模板引擎

配置模板引擎

设置模板目录和引擎

javascript
const express = require('express');
const app = express();

// 设置模板目录
app.set('views', './views');

// 设置模板引擎
app.set('view engine', 'ejs');

项目结构

myapp/
├── package.json
├── app.js
├── views/
│   ├── index.ejs
│   ├── user.ejs
│   └── layouts/
│       └── main.ejs
└── public/
    ├── css/
    └── js/

EJS模板引擎

安装和配置

bash
npm install ejs
javascript
const express = require('express');
const app = express();

app.set('view engine', 'ejs');
app.set('views', './views');

EJS基本语法

html
<!-- views/index.ejs -->
<!DOCTYPE html>
<html>
<head>
    <title><%= title %></title>
</head>
<body>
    <h1><%= message %></h1>
    
    <!-- 条件渲染 -->
    <% if (user) { %>
        <p>Welcome, <%= user.name %>!</p>
    <% } else { %>
        <p>Please log in.</p>
    <% } %>
    
    <!-- 循环渲染 -->
    <ul>
    <% users.forEach(function(user) { %>
        <li><%= user.name %> - <%= user.email %></li>
    <% }); %>
    </ul>
    
    <!-- 输出HTML(不转义) -->
    <div><%- rawHtml %></div>
    
    <!-- 输出转义HTML -->
    <div><%= safeHtml %></div>
</body>
</html>

渲染EJS模板

javascript
app.get('/', (req, res) => {
  res.render('index', {
    title: 'My App',
    message: 'Hello World',
    user: { name: 'John', email: 'john@example.com' },
    users: [
      { name: 'Alice', email: 'alice@example.com' },
      { name: 'Bob', email: 'bob@example.com' }
    ],
    rawHtml: '<script>alert("XSS")</script>', // 不转义
    safeHtml: '<strong>Bold text</strong>'     // 转义
  });
});

EJS布局和包含

html
<!-- views/partials/header.ejs -->
<header>
    <nav>
        <a href="/">Home</a>
        <a href="/about">About</a>
    </nav>
</header>

<!-- views/layouts/main.ejs -->
<!DOCTYPE html>
<html>
<head>
    <title><%= title %></title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <%- include('partials/header') %>
    <main>
        <%- body %>
    </main>
    <%- include('partials/footer') %>
</body>
</html>

<!-- views/index.ejs -->
<div>
    <h1><%= message %></h1>
    <p>Welcome to our site!</p>
</div>

Pug模板引擎

安装和配置

bash
npm install pug
javascript
const express = require('express');
const app = express();

app.set('view engine', 'pug');
app.set('views', './views');

Pug基本语法

pug
//- views/index.pug
doctype html
html
  head
    title= title
  body
    h1= message
    
    if user
      p Welcome, #{user.name}!
    else
      p Please log in.
    
    ul
      each user in users
        li= user.name + ' - ' + user.email
    
    //- 包含其他文件
    include ./partials/header
    
    block content

渲染Pug模板

javascript
app.get('/pug', (req, res) => {
  res.render('index', {
    title: 'Pug Template',
    message: 'Hello from Pug',
    user: { name: 'John', email: 'john@example.com' },
    users: [
      { name: 'Alice', email: 'alice@example.com' },
      { name: 'Bob', email: 'bob@example.com' }
    ]
  });
});

Handlebars模板引擎

安装和配置

bash
npm install express-handlebars
javascript
const express = require('express');
const { engine } = require('express-handlebars');
const app = express();

app.engine('handlebars', engine());
app.set('view engine', 'handlebars');
app.set('views', './views');

Handlebars基本语法

handlebars
<!-- views/index.handlebars -->
<!DOCTYPE html>
<html>
<head>
    <title>{{title}}</title>
</head>
<body>
    <h1>{{message}}</h1>
    
    {{#if user}}
        <p>Welcome, {{user.name}}!</p>
    {{else}}
        <p>Please log in.</p>
    {{/if}}
    
    <ul>
    {{#each users}}
        <li>{{this.name}} - {{this.email}}</li>
    {{/each}}
    </ul>
    
    <!-- 自定义助手 -->
    <p>Current year: {{currentYear}}</p>
</body>
</html>

自定义助手

javascript
const { engine } = require('express-handlebars');

app.engine('handlebars', engine({
  helpers: {
    currentYear: () => new Date().getFullYear(),
    upper: (str) => str.toUpperCase(),
    truncate: (str, length) => {
      if (str.length > length) {
        return str.substring(0, length) + '...';
      }
      return str;
    }
  }
}));

模板数据传递

基本数据传递

javascript
app.get('/user/:id', (req, res) => {
  const userId = req.params.id;
  // 模拟从数据库获取用户数据
  const user = {
    id: userId,
    name: 'John Doe',
    email: 'john@example.com',
    createdAt: new Date(),
    isActive: true
  };
  
  res.render('user', { 
    user,
    title: `User Profile - ${user.name}`,
    isAdmin: false
  });
});

全局模板变量

javascript
// 设置全局变量
app.locals.siteName = 'My Website';
app.locals.version = '1.0.0';
app.locals.currentYear = new Date().getFullYear();

// 或者设置函数
app.locals.getCurrentTime = () => new Date().toLocaleString();

中间件传递数据

javascript
// 中间件设置模板变量
app.use((req, res, next) => {
  res.locals.currentUser = req.user || null;
  res.locals.requestTime = new Date().toISOString();
  next();
});

模板继承和布局

EJS布局系统

javascript
// 安装express-ejs-layouts
const expressLayouts = require('express-ejs-layouts');

app.use(expressLayouts);
app.set('layout', './layouts/main'); // 默认布局
html
<!-- views/layouts/main.ejs -->
<!DOCTYPE html>
<html>
<head>
    <title><%= title %> - <%= siteName %></title>
    <%- include('./partials/head') %>
</head>
<body>
    <%- include('./partials/header') %>
    
    <div class="container">
        <%- body %>
    </div>
    
    <%- include('./partials/footer') %>
</body>
</html>

部分视图

javascript
// 传递特定布局
app.get('/admin', (req, res) => {
  res.render('admin/index', { 
    title: 'Admin Panel',
    layout: 'layouts/admin'  // 使用特定布局
  });
});

模板安全

XSS防护

javascript
// EJS中 <%= %> 会自动转义HTML
// 使用 <%- %> 时要特别小心,它不会转义

app.get('/user/:id', (req, res) => {
  const userInput = req.params.id; // 来自用户输入
  
  res.render('user', {
    // 确保用户输入经过验证和清理
    content: userInput.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
  });
});

内容安全策略

javascript
const helmet = require('helmet');

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    styleSrc: ["'self'", "'unsafe-inline'"],
    scriptSrc: ["'self'"]
  }
}));

模板性能优化

模板缓存

javascript
// 在生产环境中启用模板缓存
app.set('view cache', true);

// 或者根据环境设置
app.set('view cache', process.env.NODE_ENV === 'production');

预编译模板

javascript
// 对于高性能需求,可以预编译模板
const template = require('lodash.template');
const fs = require('fs');

// 预编译模板
const compiledTemplate = template(
  fs.readFileSync('./views/cached-template.ejs', 'utf8')
);

app.get('/cached', (req, res) => {
  const html = compiledTemplate({
    data: { message: 'Cached template' }
  });
  res.send(html);
});

高级模板功能

自定义模板引擎

javascript
// 注册自定义模板引擎
app.engine('ntl', (filePath, options, callback) => {
  fs.readFile(filePath, (err, content) => {
    if (err) return callback(err);
    
    // 自定义模板处理逻辑
    const rendered = content.toString()
      .replace('#title#', '<title>' + options.title + '</title>')
      .replace('#message#', '<h1>' + options.message + '</h1>');
    
    return callback(null, rendered);
  });
});

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

模板助手函数

javascript
// 创建助手函数
const templateHelpers = {
  formatDate: (date) => {
    return new Date(date).toLocaleDateString();
  },
  
  formatCurrency: (amount) => {
    return new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD'
    }).format(amount);
  },
  
  isActive: (date) => {
    return new Date(date) > new Date();
  }
};

app.use((req, res, next) => {
  res.locals.helpers = templateHelpers;
  next();
});

错误处理

javascript
// 模板错误处理
app.get('/template-error', (req, res) => {
  res.render('nonexistent-template', { title: 'Test' }, (err, html) => {
    if (err) {
      console.error('Template error:', err);
      res.status(500).send('Template rendering error');
    } else {
      res.send(html);
    }
  });
});

// 全局模板错误处理
app.use((err, req, res, next) => {
  if (err.message.includes('Failed to lookup view')) {
    res.status(404).render('errors/404', { title: 'Page Not Found' });
  } else {
    res.status(500).render('errors/500', { title: 'Server Error' });
  }
});

模板引擎是构建动态Web应用的关键组件,通过合理使用模板引擎,可以创建结构清晰、易于维护的用户界面。