模板引擎
模板引擎的基本概念
模板引擎是一种将数据和模板结合生成最终输出的工具。它允许开发者将动态内容嵌入到静态模板中,通过特定的语法标记动态部分,在运行时替换为实际数据。Node.js环境中有多种模板引擎可供选择,每种都有其独特的语法和特性。
// 一个简单的模板示例
const template = "Hello, <%= name %>!";
const data = { name: "World" };
// 经过模板引擎处理后输出: "Hello, World!"
主流Node.js模板引擎
EJS (Embedded JavaScript)
EJS是一种简单的模板语言,使用JavaScript语法嵌入到HTML中。它的语法接近原生JavaScript,学习曲线平缓。
// EJS模板示例
<ul>
<% users.forEach(function(user){ %>
<li><%= user.name %></li>
<% }); %>
</ul>
EJS支持以下主要语法:
<% %>
执行JavaScript代码但不输出<%= %>
输出转义后的值<%- %>
输出原始HTML(不转义)
Pug (原名Jade)
Pug采用缩进式的语法,减少了模板中的冗余字符,使代码更加简洁。
// Pug模板示例
ul
each user in users
li= user.name
Pug特点包括:
- 基于缩进的层级结构
- 支持混合(Mixin)和继承(Extend)
- 自动闭合标签
- 丰富的过滤器系统
Handlebars
Handlebars是一种无逻辑模板引擎,强调模板与业务逻辑的分离。
<!-- Handlebars模板示例 -->
<ul>
{{#each users}}
<li>{{name}}</li>
{{/each}}
</ul>
Handlebars核心特性:
{{expression}}
输出表达式{{#helper}}
块级助手{{> partial}}
引入部分模板- 支持自定义助手函数
模板引擎的工作原理
模板引擎通常分为以下几个处理阶段:
- 解析阶段:将模板字符串转换为抽象语法树(AST)
- 编译阶段:将AST转换为可执行的JavaScript函数
- 执行阶段:调用生成的函数并传入数据,生成最终输出
// 简化的模板引擎实现原理
function compile(template) {
const tokens = template.split(/(<%=\s*[\w\.]+\s*%>)/);
let code = 'let output = "";\n';
tokens.forEach(token => {
if (token.match(/^<%=\s*([\w\.]+)\s*%>$/)) {
const variable = token.match(/<%=\s*([\w\.]+)\s*%>/)[1];
code += `output += ${variable};\n`;
} else {
code += `output += ${JSON.stringify(token)};\n`;
}
});
code += 'return output;';
return new Function('data', code);
}
高级模板功能
模板继承与包含
现代模板引擎通常支持模板继承机制,允许创建基础模板和扩展模板。
// Pug中的模板继承
// base.pug
html
head
block title
title Default Title
body
block content
// home.pug
extends base.pug
block title
title Home Page
block content
h1 Welcome to the Home Page
自定义过滤器
一些模板引擎允许定义自定义过滤器来处理输出内容。
// EJS自定义过滤器示例
const ejs = require('ejs');
ejs.filters.uppercase = function(str) {
return str.toUpperCase();
};
// 模板中使用
// <%= name | uppercase %>
条件渲染与循环
模板引擎提供了条件判断和循环结构来处理复杂的数据展示逻辑。
<!-- Handlebars条件与循环 -->
{{#if isAdmin}}
<button class="admin">Admin Panel</button>
{{else}}
<button class="user">User Panel</button>
{{/if}}
<ul>
{{#each items}}
<li class="{{#if @first}}first{{/if}} {{#if @last}}last{{/if}}">
{{@index}}: {{this}}
</li>
{{/each}}
</ul>
性能优化技巧
预编译模板
大多数模板引擎支持预编译模板以提高运行时性能。
// EJS预编译示例
const ejs = require('ejs');
const template = ejs.compile('<%= message %>', {client: true});
// 之后可以多次使用编译后的模板
console.log(template({message: 'Hello'}));
缓存机制
合理使用模板缓存可以显著提高应用性能。
// Express中使用EJS缓存
app.set('view cache', true);
app.set('view engine', 'ejs');
部分渲染
只更新需要变化的部分而不是整个模板。
// 实现部分模板更新
function updatePartial(templateId, data) {
const template = document.getElementById(templateId).innerHTML;
const rendered = ejs.render(template, data);
document.getElementById('target').innerHTML = rendered;
}
安全注意事项
XSS防护
模板引擎通常提供自动转义功能来防止XSS攻击。
// EJS中的自动转义
// <%= userInput %> 会自动转义HTML
// <%- userInput %> 会输出原始HTML,慎用
模板注入防护
避免将用户输入直接作为模板内容。
// 不安全的做法
const userTemplate = req.query.template;
ejs.render(userTemplate, data); // 可能导致模板注入
// 安全的做法
const safeTemplates = {
'template1': '<%= safeData %>',
'template2': '<%- trustedData %>'
};
ejs.render(safeTemplates[req.query.template], data);
与其他技术的集成
与Express框架集成
大多数模板引擎都提供了与Express的无缝集成。
// Express配置EJS
const express = require('express');
const app = express();
app.set('views', './views');
app.set('view engine', 'ejs');
app.get('/', (req, res) => {
res.render('index', { title: 'Home Page' });
});
与前端框架结合
模板引擎也可以在前端使用,与现代前端框架配合。
// 在React中使用EJS
function renderEjsInReact(template, data) {
const html = ejs.render(template, data);
return <div dangerouslySetInnerHTML={{__html: html}} />;
}
静态网站生成
模板引擎常用于静态网站生成器。
// 使用模板引擎生成静态页面
const fs = require('fs');
const template = fs.readFileSync('template.ejs', 'utf8');
const html = ejs.render(template, {pageTitle: 'About Us'});
fs.writeFileSync('about.html', html);
实际应用场景
动态电子邮件模板
// 生成电子邮件内容
const emailTemplate = `
<h1>Hello <%= user.name %>,</h1>
<p>Your order #<%= order.id %> has been shipped.</p>
<p>Tracking number: <%= order.trackingNumber %></p>
`;
const emailContent = ejs.render(emailTemplate, {
user: {name: 'John Doe'},
order: {id: '12345', trackingNumber: 'ZYX987'}
});
多语言支持
// 多语言模板处理
const locales = {
en: {
welcome: 'Welcome, <%= name %>!',
products: 'Our Products'
},
es: {
welcome: '¡Bienvenido, <%= name %>!',
products: 'Nuestros Productos'
}
};
function renderLocalized(templateKey, lang, data) {
return ejs.render(locales[lang][templateKey], data);
}
配置文件的动态生成
// 生成配置文件
const configTemplate = `
server {
listen <%= port %>;
server_name <%= domain %>;
location / {
root <%= root %>;
index index.html;
}
}
`;
const nginxConfig = ejs.render(configTemplate, {
port: 8080,
domain: 'example.com',
root: '/var/www/html'
});
自定义模板引擎开发
创建一个简单的模板引擎可以帮助深入理解其工作原理。
class SimpleTemplateEngine {
constructor(delimiters = ['{{', '}}']) {
this.delimiters = delimiters;
}
compile(template) {
const [open, close] = this.delimiters;
const pattern = new RegExp(`${open}\\s*(.+?)\\s*${close}`, 'g');
const code = [
'let output = [];',
`with(data) {`,
` output.push(\`${template.replace(pattern, '\');\n output.push($1);\n output.push(`')}\`);`,
`}`,
`return output.join('');`
].join('\n');
return new Function('data', code);
}
render(template, data) {
const compiled = this.compile(template);
return compiled(data);
}
}
// 使用示例
const engine = new SimpleTemplateEngine();
const result = engine.render('Hello {{name}}!', {name: 'World'});
console.log(result); // 输出: Hello World!
模板引擎的调试技巧
错误追踪
// 在EJS中启用调试
ejs.renderFile('template.ejs', data, {
debug: true,
compileDebug: true
}, (err, html) => {
if (err) {
console.error('Template error:', err);
return;
}
console.log(html);
});
上下文检查
// 在模板中检查可用数据
/*
<% console.log('Template data:', Object.keys(data)); %>
<% if (!data.user) { %>
<p>Warning: user data is missing</p>
<% } %>
*/
性能分析
// 测量模板渲染时间
console.time('template-render');
ejs.render(template, largeData);
console.timeEnd('template-render');
未来发展趋势
模板引擎与Web Components
// 使用模板引擎定义Web Components
class MyElement extends HTMLElement {
constructor() {
super();
const template = `
<style>
:host {
display: block;
}
</style>
<div class="container">
<slot></slot>
</div>
`;
this.attachShadow({mode: 'open'}).innerHTML = template;
}
}
customElements.define('my-element', MyElement);
服务端渲染(SSR)中的应用
// 在Next.js中使用EJS
import ejs from 'ejs';
import fs from 'fs';
import path from 'path';
export async function getServerSideProps() {
const templatePath = path.join(process.cwd(), 'templates', 'page.ejs');
const template = fs.readFileSync(templatePath, 'utf8');
const html = ejs.render(template, { title: 'SSR Page' });
return {
props: { html },
};
}
function Page({ html }) {
return <div dangerouslySetInnerHTML={{ __html: html }} />;
}
静态类型检查集成
// 为模板数据定义TypeScript接口
interface TemplateData {
title: string;
items: Array<{
id: number;
name: string;
price: number;
}>;
showPrice: boolean;
}
function renderTemplate(data: TemplateData): string {
return ejs.render(templateString, data);
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn