阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 模板引擎

模板引擎

作者:陈川 阅读数:62594人阅读 分类: Node.js

模板引擎的基本概念

模板引擎是一种将数据和模板结合生成最终输出的工具。它允许开发者将动态内容嵌入到静态模板中,通过特定的语法标记动态部分,在运行时替换为实际数据。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}} 引入部分模板
  • 支持自定义助手函数

模板引擎的工作原理

模板引擎通常分为以下几个处理阶段:

  1. 解析阶段:将模板字符串转换为抽象语法树(AST)
  2. 编译阶段:将AST转换为可执行的JavaScript函数
  3. 执行阶段:调用生成的函数并传入数据,生成最终输出
// 简化的模板引擎实现原理
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

上一篇:ORM工具使用

下一篇:构建工具

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌