Appearance
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基本语法
自定义助手
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应用的关键组件,通过合理使用模板引擎,可以创建结构清晰、易于维护的用户界面。