The template engine translates this sentence into English.
Basic Concepts of Template Engines
A template engine is a tool that combines data with templates to generate final output. It allows developers to embed dynamic content into static templates by using specific syntax markers for dynamic parts, which are replaced with actual data at runtime. In the Node.js environment, there are various template engines to choose from, each with its unique syntax and features.
// A simple template example
const template = "Hello, <%= name %>!";
const data = { name: "World" };
// After processing by the template engine, the output is: "Hello, World!"
Mainstream Node.js Template Engines
EJS (Embedded JavaScript)
EJS is a simple templating language that embeds JavaScript syntax into HTML. Its syntax is close to native JavaScript, making it easy to learn.
// EJS template example
<ul>
<% users.forEach(function(user){ %>
<li><%= user.name %></li>
<% }); %>
</ul>
EJS supports the following main syntax:
<% %>
Executes JavaScript code but does not output<%= %>
Outputs escaped values<%- %>
Outputs raw HTML (unescaped)
Pug (formerly Jade)
Pug uses an indentation-based syntax, reducing redundant characters in templates and making the code more concise.
// Pug template example
ul
each user in users
li= user.name
Pug features include:
- Indentation-based hierarchical structure
- Support for mixins and inheritance
- Auto-closing tags
- Rich filter system
Handlebars
Handlebars is a logic-less templating engine that emphasizes the separation of templates and business logic.
<!-- Handlebars template example -->
<ul>
{{#each users}}
<li>{{name}}</li>
{{/each}}
</ul>
Handlebars core features:
{{expression}}
Outputs expressions{{#helper}}
Block helpers{{> partial}}
Includes partial templates- Supports custom helper functions
How Template Engines Work
Template engines typically go through the following processing stages:
- Parsing Phase: Converts the template string into an Abstract Syntax Tree (AST)
- Compilation Phase: Transforms the AST into executable JavaScript functions
- Execution Phase: Calls the generated functions with input data to produce the final output
// Simplified implementation of a template engine
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);
}
Advanced Template Features
Template Inheritance and Inclusion
Modern template engines often support inheritance mechanisms, allowing the creation of base templates and extended templates.
// Template inheritance in 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
Custom Filters
Some template engines allow defining custom filters to process output content.
// EJS custom filter example
const ejs = require('ejs');
ejs.filters.uppercase = function(str) {
return str.toUpperCase();
};
// Usage in templates
// <%= name | uppercase %>
Conditional Rendering and Loops
Template engines provide conditional statements and loop structures to handle complex data display logic.
<!-- Handlebars conditionals and loops -->
{{#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>
Performance Optimization Techniques
Precompiling Templates
Most template engines support precompiling templates to improve runtime performance.
// EJS precompilation example
const ejs = require('ejs');
const template = ejs.compile('<%= message %>', {client: true});
// The compiled template can be reused multiple times
console.log(template({message: 'Hello'}));
Caching Mechanisms
Proper use of template caching can significantly improve application performance.
// Using EJS caching in Express
app.set('view cache', true);
app.set('view engine', 'ejs');
Partial Rendering
Only update the parts of the template that need to change, rather than the entire template.
// Implementing partial template updates
function updatePartial(templateId, data) {
const template = document.getElementById(templateId).innerHTML;
const rendered = ejs.render(template, data);
document.getElementById('target').innerHTML = rendered;
}
Security Considerations
XSS Protection
Template engines typically provide automatic escaping to prevent XSS attacks.
// Automatic escaping in EJS
// <%= userInput %> Automatically escapes HTML
// <%- userInput %> Outputs raw HTML—use with caution
Template Injection Protection
Avoid using user input directly as template content.
// Unsafe practice
const userTemplate = req.query.template;
ejs.render(userTemplate, data); // May lead to template injection
// Safe practice
const safeTemplates = {
'template1': '<%= safeData %>',
'template2': '<%- trustedData %>'
};
ejs.render(safeTemplates[req.query.template], data);
Integration with Other Technologies
Integration with Express Framework
Most template engines offer seamless integration with Express.
// Configuring EJS in Express
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' });
});
Combining with Frontend Frameworks
Template engines can also be used on the frontend, alongside modern frontend frameworks.
// Using EJS in React
function renderEjsInReact(template, data) {
const html = ejs.render(template, data);
return <div dangerouslySetInnerHTML={{__html: html}} />;
}
Static Site Generation
Template engines are commonly used in static site generators.
// Generating static pages with a template engine
const fs = require('fs');
const template = fs.readFileSync('template.ejs', 'utf8');
const html = ejs.render(template, {pageTitle: 'About Us'});
fs.writeFileSync('about.html', html);
Practical Use Cases
Dynamic Email Templates
// Generating email content
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'}
});
Multilingual Support
// Handling multilingual templates
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);
}
Dynamic Configuration File Generation
// Generating configuration files
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'
});
Developing Custom Template Engines
Creating a simple template engine can help deepen understanding of how they work.
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);
}
}
// Usage example
const engine = new SimpleTemplateEngine();
const result = engine.render('Hello {{name}}!', {name: 'World'});
console.log(result); // Output: Hello World!
Debugging Techniques for Template Engines
Error Tracing
// Enabling debugging in EJS
ejs.renderFile('template.ejs', data, {
debug: true,
compileDebug: true
}, (err, html) => {
if (err) {
console.error('Template error:', err);
return;
}
console.log(html);
});
Context Inspection
// Inspecting available data in templates
/*
<% console.log('Template data:', Object.keys(data)); %>
<% if (!data.user) { %>
<p>Warning: user data is missing</p>
<% } %>
*/
Performance Profiling
// Measuring template rendering time
console.time('template-render');
ejs.render(template, largeData);
console.timeEnd('template-render');
Future Trends
Template Engines and Web Components
// Defining Web Components with template engines
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);
Applications in Server-Side Rendering (SSR)
// Using EJS in Next.js
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 }} />;
}
Static Type Checking Integration
// Defining TypeScript interfaces for template data
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