您现在的位置是:网站首页 > 模板引擎文章详情
模板引擎
陈川
【
Node.js
】
54229人已围观
6699字
模板引擎的基本概念
模板引擎是一种将数据和模板结合生成最终输出的工具。它允许开发者将动态内容嵌入到静态模板中,通过特定的语法标记动态部分,在运行时将数据填充到模板中生成最终的HTML、XML或其他文本格式。在Node.js环境中,模板引擎特别有用,因为它可以帮助开发者高效地构建动态网页。
模板引擎的核心功能包括:
- 变量插值:将数据中的值插入到模板的指定位置
- 条件判断:根据数据的不同值决定显示哪些内容
- 循环结构:遍历数组或对象生成重复的HTML结构
- 模板继承:创建基础模板并在子模板中扩展或覆盖部分内容
- 局部模板:将重复使用的HTML片段提取为可复用的组件
Node.js中常用的模板引擎
Node.js生态系统中有多种模板引擎可供选择,每种都有其特点和适用场景:
EJS (Embedded JavaScript)
EJS是一种简单的模板语言,使用JavaScript语法嵌入到HTML中。它的语法与常规JavaScript非常相似,学习曲线平缓。
// 示例:EJS模板
<h1><%= title %></h1>
<ul>
<% users.forEach(function(user){ %>
<li><%= user.name %></li>
<% }); %>
</ul>
Pug (原名Jade)
Pug是一种基于缩进的模板引擎,它使用简洁的语法减少了HTML的冗余代码。Pug模板编译为HTML,支持模板继承和混合。
// 示例:Pug模板
h1= title
ul
each user in users
li= user.name
Handlebars
Handlebars是一种无逻辑模板引擎,强调模板与业务逻辑的分离。它使用双花括号语法,支持助手函数和局部模板。
<!-- 示例:Handlebars模板 -->
<h1>{{title}}</h1>
<ul>
{{#each users}}
<li>{{name}}</li>
{{/each}}
</ul>
Nunjucks
Nunjucks是受Jinja2启发的模板引擎,功能丰富,支持模板继承、异步控制和自定义过滤器。
{# 示例:Nunjucks模板 #}
<h1>{{ title }}</h1>
<ul>
{% for user in users %}
<li>{{ user.name }}</li>
{% endfor %}
</ul>
模板引擎的核心功能详解
变量插值与转义
变量插值是模板引擎最基本的功能,将数据中的值输出到模板中。为了防止XSS攻击,大多数模板引擎默认会对输出的内容进行HTML转义。
// EJS中的变量插值
<p><%= userInput %></p> // 自动转义
<p><%- rawHtml %></p> // 不转义,慎用
条件判断
模板引擎允许根据条件显示不同的内容块,这在实际应用中非常常见。
<!-- Handlebars中的条件判断 -->
{{#if isAdmin}}
<button>删除</button>
{{else}}
<button>查看</button>
{{/if}}
循环结构
处理列表数据是模板引擎的强项,可以轻松遍历数组或对象生成重复的HTML结构。
// Pug中的循环
ul
each item, index in items
li= `${index + 1}. ${item.name}`
模板继承与包含
模板继承允许创建基础模板,子模板可以扩展或覆盖基础模板的部分内容,实现代码复用。
{# Nunjucks中的模板继承 #}
{# base.html #}
<html>
<head>
<title>{% block title %}默认标题{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
{# child.html #}
{% extends "base.html" %}
{% block title %}子页面标题{% endblock %}
{% block content %}
<h1>这是子页面内容</h1>
{% endblock %}
自定义过滤器与助手
高级模板引擎允许定义自定义过滤器或助手函数,扩展模板的功能。
// EJS中的自定义过滤器
const ejs = require('ejs');
ejs.filters.currency = function(amount) {
return '$' + parseFloat(amount).toFixed(2);
};
// 模板中使用
<p>价格: <%=: price | currency %></p>
模板引擎的性能优化
预编译模板
大多数模板引擎支持预编译模板为JavaScript函数,这样可以显著提高渲染性能。
// Handlebars预编译示例
const Handlebars = require('handlebars');
const template = Handlebars.compile('<h1>{{title}}</h1>');
const html = template({ title: 'Hello' });
缓存机制
重复渲染相同模板时,使用缓存可以避免重复解析模板字符串。
// EJS中的缓存
app.set('view cache', true); // Express中启用视图缓存
合理使用局部模板
将重复使用的UI组件提取为局部模板,可以减少代码重复并提高维护性。
<!-- 注册局部模板 -->
{{#*inline "userCard"}}
<div class="user-card">
<h3>{{name}}</h3>
<p>{{email}}</p>
</div>
{{/inline}}
<!-- 使用局部模板 -->
{{> userCard}}
模板引擎的安全考虑
XSS防护
模板引擎通常会自动转义HTML内容,但开发者需要注意不转义的情况。
// 危险示例:直接输出未转义的用户输入
<p><%- userInput %></p> // 可能包含恶意脚本
模板注入防护
避免将用户输入直接作为模板内容或模板路径,防止模板注入攻击。
// 危险示例:动态加载用户提供的模板路径
const template = require('path').join(__dirname, req.query.template);
上下文感知转义
根据输出上下文(HTML, JavaScript, CSS, URL)采用不同的转义策略。
// 上下文相关转义示例
const escape = require('escape-html');
const jsEscape = require('js-string-escape');
const userInput = '<script>alert(1)</script>';
const safeHtml = escape(userInput);
const safeJs = jsEscape(userInput);
模板引擎与前端框架的结合
服务端渲染(SSR)
模板引擎可以用于实现服务端渲染,提高首屏加载速度和SEO。
// Express中使用EJS进行SSR
app.get('/', (req, res) => {
res.render('index', {
title: '服务端渲染',
items: ['Item 1', 'Item 2']
});
});
同构应用
结合客户端JavaScript,可以实现同构应用,同一套模板在服务端和客户端都能渲染。
// 同构应用示例
// server.js
app.get('/', (req, res) => {
const html = renderToString(<App />);
res.render('layout', { html });
});
// client.js
hydrateRoot(document.getElementById('root'), <App />);
渐进式增强
使用模板引擎生成基础HTML结构,然后通过JavaScript增强交互性。
<!-- 渐进式增强示例 -->
<button id="load-more" class="btn" data-page="1">
加载更多
</button>
<script>
document.getElementById('load-more').addEventListener('click', async () => {
const page = this.dataset.page;
const response = await fetch(`/api/items?page=${page}`);
const items = await response.json();
// 更新DOM...
});
</script>
高级模板技巧
动态模板选择
根据条件动态选择不同的模板文件。
// Express中动态选择模板
app.get('/profile', (req, res) => {
const template = req.user.isAdmin ? 'admin-profile' : 'user-profile';
res.render(template, { user: req.user });
});
自定义模板标签
一些模板引擎允许自定义标签语法,以适应特定需求。
// EJS中自定义分隔符
const ejs = require('ejs');
ejs.delimiter = '?';
ejs.openDelimiter = '[';
ejs.closeDelimiter = ']';
// 模板中使用新分隔符
<h1>[= title ]</h1>
国际化支持
模板引擎可以结合国际化库实现多语言支持。
<!-- 国际化示例 -->
<h1>{{__ 'welcome_message'}}</h1>
<p>{{__ 'current_date'}} {{formatDate now}}</p>
流式渲染
对于大型页面,可以使用流式渲染逐步输出内容,减少内存使用。
// Express中使用EJS流式渲染
app.get('/large-page', (req, res) => {
const data = { /* 大量数据 */ };
ejs.renderFile('views/large-page.ejs', data, {}, (err, str) => {
if (err) return res.status(500).send(err);
res.send(str);
});
});
模板引擎的调试技巧
模板错误定位
当模板出现错误时,准确找到问题位置是关键。
// EJS中启用严格模式
const ejs = require('ejs');
ejs.renderFile('template.ejs', {}, { strict: true }, (err, html) => {
if (err) {
console.error('Line:', err.line);
console.error('Column:', err.column);
console.error('Token:', err.token);
}
});
上下文数据检查
在模板中输出当前上下文数据,帮助调试。
<!-- 输出上下文数据 -->
<pre>{{json context}}</pre>
<!-- 自定义助手 -->
Handlebars.registerHelper('json', function(context) {
return JSON.stringify(context, null, 2);
});
模板缓存清除
开发过程中,可能需要清除模板缓存以查看最新更改。
// 清除EJS缓存
Object.keys(require.cache).forEach(key => {
if (/\.ejs$/.test(key)) delete require.cache[key];
});
模板引擎的未来发展
模板引擎与Web Components
现代模板引擎开始支持Web Components,实现更好的组件化开发。
<!-- 使用Web Components -->
<user-profile name="{{name}}" avatar="{{avatarUrl}}"></user-profile>
静态站点生成
模板引擎在静态站点生成(SSG)工具中扮演重要角色,如Eleventy、Hugo等。
// Eleventy中使用Nunjucks
module.exports = function(eleventyConfig) {
return {
dir: {
input: "src",
output: "dist"
}
};
};
模板引擎与GraphQL
结合GraphQL,模板引擎可以更精确地获取所需数据。
// GraphQL查询与模板渲染
const query = `
query {
post(id: 123) {
title
content
author {
name
}
}
}
`;
fetch('/graphql', {
method: 'POST',
body: JSON.stringify({ query })
})
.then(res => res.json())
.then(data => {
const html = template(data.post);
});