您现在的位置是:网站首页 > 模板引擎文章详情

模板引擎

模板引擎的基本概念

模板引擎是一种将数据和模板结合生成最终输出的工具。它允许开发者将动态内容嵌入到静态模板中,通过特定的语法标记动态部分,在运行时将数据填充到模板中生成最终的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);
});

上一篇: ORM工具使用

下一篇: 构建工具

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步