当前位置: 首页 > news >正文

EJS教程

EJS教程

简介

EJS (Embedded JavaScript)是一种简单而强大的JavaScript模板引擎,它让你能够在HTML页面中嵌入JavaScript代码。与其他模板引擎相比,EJS的语法非常接近原生JavaScript,学习曲线较低,特别适合已经熟悉JavaScript的开发者。

EJS的主要特点:

  • 快速编译和渲染
  • 简单的模板标签
  • 支持自定义分隔符
  • 包含客户端支持
  • 支持Express框架
  • 没有新的语法需要学习,直接使用JavaScript

安装和设置

在Node.js项目中安装EJS

npm install ejs

基本使用方法

在Node.js应用中:

const ejs = require('ejs');
const fs = require('fs');// 方法1:从字符串渲染
let template = '<h1><%= title %></h1>';
let data = { title: 'EJS模板引擎' };
let html = ejs.render(template, data);console.log(html); // 输出: <h1>EJS模板引擎</h1>// 方法2:从文件渲染
ejs.renderFile('template.ejs', data, (err, html) => {if (err) {console.log(err);} else {console.log(html);}
});

在Express框架中使用

const express = require('express');
const app = express();// 设置EJS为视图引擎
app.set('view engine', 'ejs');
app.set('views', './views'); // 指定模板文件目录// 路由处理
app.get('/', (req, res) => {res.render('index', { title: 'EJS模板引擎' });
});app.listen(3000, () => {console.log('服务器运行在 http://localhost:3000');
});

基本语法

EJS使用标签来嵌入JavaScript代码。基本标签形式如下:

标签描述
<% %>执行JavaScript代码,不输出任何内容
<%= %>输出转义后的HTML内容
<%- %>输出原始HTML内容(不转义)
<%# %>注释标签,不执行也不输出
<%_ %>删除前面的空白字符
<% _%>删除后面的空白字符
<%%输出字面的 ‘<%’
%%>输出字面的 ‘%>’

EJS标签

1. 代码执行标签 <% %>

这种标签用于执行JavaScript代码,但不输出任何内容。

<% let name = 'World'; %>
<% if (user) { %><h2>Hello, <%= user.name %></h2>
<% } %>

2. 输出转义内容标签 <%= %>

输出转义后的内容(防止XSS攻击)。

<h1>Hello, <%= name %></h1>

如果name包含<script>等HTML标签,会被转义为&lt;script&gt;

3. 输出原始内容标签 <%- %>

输出未转义的原始HTML内容。

<%- include('header') %>
<%- htmlContent %>

警告:使用<%-时要确保内容安全,因为它会按原样输出HTML,可能导致XSS攻击。

4. 注释标签 <%# %>

<%# 这是一个注释,不会被输出 %>

5. 控制空白字符的标签 <%__%>

<%_ for(let i=0; i<5; i++) { _%><li><%= i %></li>
<%_ } _%>

条件语句

在EJS中使用条件语句和JavaScript中完全一样:

<% if (user) { %><h2>Hello, <%= user.name %></h2>
<% } else { %><h2>请登录</h2>
<% } %><% let status = user.status;switch(status) {case 'admin': 
%><span class="admin-badge">管理员</span>
<% break;case 'member': 
%><span class="member-badge">会员</span>
<% break;default: 
%><span class="guest-badge">访客</span>
<% break;} 
%>

循环语句

EJS中的循环也使用标准JavaScript语法:

for循环

<ul>
<% for(let i=0; i<items.length; i++) { %><li><%= items[i].name %></li>
<% } %>
</ul>

forEach循环

<ul>
<% items.forEach(function(item) { %><li><%= item.name %> - <%= item.price %>元</li>
<% }); %>
</ul>

map方法

<ul>
<% users.map(user => { %><li id="user-<%= user.id %>"><%= user.name %></li>
<% }); %>
</ul>

包含其他文件

EJS允许你包含其他模板文件,非常适合创建可复用的组件:

<%- include('header', { title: '我的网站' }) %><main><h1>欢迎访问</h1><p>这是主要内容</p>
</main><%- include('footer', { year: new Date().getFullYear() }) %>

包含文件时可以传递数据对象。如果不传递,则被包含的文件可以访问当前模板的所有变量。

自定义函数和过滤器

在渲染时传递函数

// Node.js代码
app.get('/', (req, res) => {res.render('index', {data: ['apple', 'banana', 'orange'],formatText: function(text) {return text.toUpperCase();}});
});
<!-- EJS模板 -->
<% data.forEach(function(item) { %><li><%= formatText(item) %></li>
<% }); %>

创建EJS过滤器(全局函数)

如果你使用Express,可以创建全局可用的函数:

// 在应用入口文件中设置
app.locals.formatDate = function(date) {const d = new Date(date);return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
};
<!-- 在任何模板中使用 -->
<p>发布时间: <%= formatDate(article.publishDate) %></p>

布局模板

EJS本身没有内置布局功能,但可以通过include和变量组合实现:

方法1:使用include和变量

main-layout.ejs:

<!DOCTYPE html>
<html>
<head><title><%= title %></title><link rel="stylesheet" href="/styles.css">
</head>
<body><%- include('header') %><main><%- body %></main><%- include('footer') %>
</body>
</html>

home.ejs:

<% let body = `<h1>首页</h1><p>欢迎访问我们的网站!</p>
`; %><%- include('main-layout', { title: '首页 - 我的网站',body: body 
}) %>

方法2:使用express-ejs-layouts

更简单的方法是使用express-ejs-layouts中间件:

npm install express-ejs-layouts
const expressLayouts = require('express-ejs-layouts');app.use(expressLayouts);
app.set('layout', 'layouts/main'); // 指定默认布局文件

layouts/main.ejs:

<!DOCTYPE html>
<html>
<head><title><%= title %></title><%- style %>
</head>
<body><%- include('../partials/header') %><main><%- body %></main><%- include('../partials/footer') %><%- script %>
</body>
</html>

然后在页面中,您可以直接编写内容,不需要包含布局:

<h1>首页内容</h1>
<p>这是首页的主要内容</p><%- contentFor('style') %>
<style>h1 {color: blue;}
</style><%- contentFor('script') %>
<script>console.log('页面加载完成');
</script>

最佳实践

1. 保持模板简洁

尽量减少模板中的业务逻辑,将复杂的逻辑放在路由处理器或单独的模块中。

2. 组织模板文件

建议的项目结构:

views/
├── layouts/      <!-- 布局模板 -->
│   └── main.ejs
├── partials/     <!-- 可复用组件 -->
│   ├── header.ejs
│   ├── footer.ejs
│   └── sidebar.ejs
└── pages/        <!-- 页面模板 -->├── home.ejs├── about.ejs└── contact.ejs

3. 使用部分视图

将重复的部分提取为部分视图,提高代码复用性。

4. 注意安全问题

  • 使用<%=而不是<%-来避免XSS攻击
  • 验证和清理用户输入
  • 避免在客户端暴露敏感信息

5. 性能优化

对于需要高性能的应用,考虑启用模板缓存:

// 在生产环境中启用视图缓存
if (process.env.NODE_ENV === 'production') {app.enable('view cache');
}

常见问题解答

Q: EJS和其他模板引擎比如Pug, Handlebars相比有什么优势?

A: EJS的主要优势是学习曲线低,使用纯JavaScript语法,没有引入新的模板语言。这让已经熟悉JavaScript的开发者可以立即上手,而不需要学习新的语法规则。

Q: 如何在EJS中处理表单提交和CSRF保护?

A: 通常使用中间件如csurf来处理CSRF保护:

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });app.get('/form', csrfProtection, (req, res) => {res.render('form', { csrfToken: req.csrfToken() });
});app.post('/process', csrfProtection, (req, res) => {// 处理表单数据
});
<form action="/process" method="post"><input type="hidden" name="_csrf" value="<%= csrfToken %>"><!-- 其他表单字段 --><button type="submit">提交</button>
</form>

Q: 如何在客户端使用EJS?

A: EJS也可以在浏览器中使用:

<script src="https://cdn.jsdelivr.net/npm/ejs@3.1.6/ejs.min.js"></script>
<script>let template = '<h1><%= title %></h1>';let data = { title: 'EJS在浏览器中' };let html = ejs.render(template, data);document.getElementById('content').innerHTML = html;
</script>

Q: 如何使用EJS自定义分隔符?

A: 可以通过选项设置自定义分隔符:

let template = '[[ title ]]';
let html = ejs.render(template, { title: 'EJS自定义分隔符' }, {delimiter: '[]'
});

Q: 如何处理EJS中的错误?

A: 在Express应用中,可以使用错误处理中间件:

app.get('/', (req, res, next) => {try {res.render('index', { data: undefined.property }); // 会产生错误} catch (err) {next(err);}
});// 错误处理中间件
app.use((err, req, res, next) => {console.error(err);res.status(500).render('error', { error: err });
});

这个教程涵盖了EJS的基本概念和高级用法。随着您的深入学习,您会发现EJS是一个既简单又强大的模板引擎,特别适合熟悉JavaScript的开发者使用。

相关文章:

  • Pyhton训练营打卡Day27
  • 03、基础入门-SpringBoot的大时代背景
  • Java8到24新特性整理
  • Mac安装Navicat16
  • Linux运行时的参数、命令、网络、磁盘参数和日志监控
  • 产品经理入门——认识产品经理
  • 数据库--向量化基础
  • 大模型相关技术综述
  • IEEE PRMVAI 2025 IEEE PRMVAI 探索人工智能在基础设施建设应用与运维中的新挑战
  • Nextjs首屏加载速度性能从80分优化到98分
  • Python OOP核心技巧:如何正确选择实例方法、类方法和静态方法
  • 【C++】15.并发支持库
  • QML 属性动画、行为动画与预定义动画
  • Flask框架搭建
  • AI编程赛道的思考:构建商业闭环Build your business,而非仅仅是应用not only build an app
  • 嵌入式学习笔记 - STM32 ADC 模块工作模式总结
  • 基于stm32f103c8t6的宠物仿声系统管理设计
  • 大模型,为什么需要分阶段学习?
  • 桌面端截长图/滚动截图:图像融合拼接关键算法
  • 【LeetCode 热题 100】动态规划 系列
  • 国际乒联主席索林:洛杉矶奥运会增设混团是里程碑事件
  • 海外考古大家访谈|冈村秀典:礼制的形成与早期中国
  • 精品消费“精”在哪?多在体验上下功夫
  • 嫩黑线货物列车脱轨致1名路外人员死亡,3人被采取刑事强制措施
  • 就规范涉企行政执法专项行动有关问题,司法部发布解答
  • 侵害孩子者,必严惩不贷!3名性侵害未成年人罪犯被执行死刑