- Home »

Как использовать EJS для шаблонизации Node.js приложений
Привет, дорогой читатель! Сегодня говорим о том, как сделать твоё Node.js приложение красивым и функциональным с помощью EJS — одного из самых популярных шаблонизаторов для сервера. Если ты уже устал от статичных HTML-страниц и хочешь динамически генерировать контент, то EJS станет твоим лучшим другом. Эта статья поможет тебе разобраться, как правильно настроить EJS, избежать типичных граблей и выжать максимум из этого инструмента.
Мы разберём три ключевых момента: как работает EJS под капотом, как быстро развернуть всё с нуля, и покажем реальные примеры использования с подводными камнями. Плюс дам несколько нестандартных способов использования, которые могут пригодиться в автоматизации.
Что такое EJS и как оно работает
EJS (Embedded JavaScript) — это шаблонизатор, который позволяет встраивать JavaScript-код прямо в HTML-разметку. Работает по принципу “что видишь, то и получаешь” — никакой магии, просто чистый JavaScript внутри HTML.
Основные теги EJS:
<% %>
— выполнение JavaScript-кода без вывода<%= %>
— вывод значения с экранированием HTML<%- %>
— вывод сырого HTML без экранирования<%# %>
— комментарии<%% %>
— вывод литерального<%
Быстрая настройка EJS в Node.js
Поехали по шагам. Для начала нужно создать проект и установить зависимости:
mkdir my-ejs-app
cd my-ejs-app
npm init -y
npm install express ejs
Создаём основной файл приложения server.js
:
const express = require('express');
const app = express();
const port = 3000;
// Настраиваем EJS как шаблонизатор
app.set('view engine', 'ejs');
app.set('views', './views');
// Middleware для статических файлов
app.use(express.static('public'));
// Основной маршрут
app.get('/', (req, res) => {
const data = {
title: 'Мой EJS сайт',
message: 'Привет от EJS!',
users: ['Алексей', 'Мария', 'Дмитрий']
};
res.render('index', data);
});
app.listen(port, () => {
console.log(`Сервер запущен на http://localhost:${port}`);
});
Создаём структуру папок и файлов:
mkdir views
mkdir public
mkdir public/css
mkdir public/js
Создаём шаблон views/index.ejs
:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<h1><%= message %></h1>
<% if (users.length > 0) { %>
<ul>
<% users.forEach(function(user) { %>
<li><%= user %></li>
<% }); %>
</ul>
<% } else { %>
<p>Пользователей нет</p>
<% } %>
</body>
</html>
Запускаем сервер:
node server.js
Продвинутые возможности EJS
Включения (Includes)
Одна из самых полезных фич — возможность включать другие шаблоны. Создаём views/partials/header.ejs
:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<nav>
<a href="/">Главная</a>
<a href="/about">О нас</a>
</nav>
И футер views/partials/footer.ejs
:
<footer>
<p>© 2024 Мой сайт</p>
</footer>
</body>
</html>
Теперь основной шаблон выглядит так:
<%- include('partials/header') %>
<main>
<h1><%= message %></h1>
<% if (users.length > 0) { %>
<ul>
<% users.forEach(function(user) { %>
<li><%= user %></li>
<% }); %>
</ul>
<% } else { %>
<p>Пользователей нет</p>
<% } %>
</main>
<%- include('partials/footer') %>
Передача данных в включения
Можно передавать данные в включаемые шаблоны:
<%- include('partials/user-card', {user: users[0], showEmail: true}) %>
Практические примеры и кейсы
Работа с формами
Пример обработки формы с валидацией:
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.post('/contact', (req, res) => {
const { name, email, message } = req.body;
const errors = [];
if (!name) errors.push('Имя обязательно');
if (!email) errors.push('Email обязателен');
if (!message) errors.push('Сообщение обязательно');
if (errors.length > 0) {
res.render('contact', {
errors,
formData: req.body,
title: 'Контакты'
});
} else {
// Обработка успешной отправки
res.render('contact', {
success: 'Сообщение отправлено!',
formData: {},
title: 'Контакты'
});
}
});
Шаблон формы views/contact.ejs
:
<%- include('partials/header') %>
<% if (typeof errors !== 'undefined' && errors.length > 0) { %>
<div class="error">
<ul>
<% errors.forEach(function(error) { %>
<li><%= error %></li>
<% }); %>
</ul>
</div>
<% } %>
<% if (typeof success !== 'undefined') { %>
<div class="success"><%= success %></div>
<% } %>
<form method="POST" action="/contact">
<input type="text" name="name" value="<%= formData.name || '' %>" placeholder="Имя">
<input type="email" name="email" value="<%= formData.email || '' %>" placeholder="Email">
<textarea name="message" placeholder="Сообщение"><%= formData.message || '' %></textarea>
<button type="submit">Отправить</button>
</form>
<%- include('partials/footer') %>
Работа с базой данных
Пример интеграции с MongoDB:
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: String,
email: String,
createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model('User', userSchema);
app.get('/users', async (req, res) => {
try {
const users = await User.find().sort({ createdAt: -1 });
res.render('users', {
users,
title: 'Пользователи'
});
} catch (error) {
res.render('users', {
users: [],
error: 'Ошибка загрузки пользователей',
title: 'Пользователи'
});
}
});
Сравнение с другими шаблонизаторами
Характеристика | EJS | Handlebars | Pug |
---|---|---|---|
Синтаксис | HTML + JavaScript | HTML + логика | Индентация |
Кривая обучения | Низкая | Средняя | Высокая |
Производительность | Высокая | Средняя | Высокая |
Расширяемость | Отличная | Хорошая | Хорошая |
Размер пакета | ~90кб | ~200кб | ~150кб |
Оптимизация производительности
Несколько важных советов для продакшена:
Кеширование шаблонов
// В продакшене всегда включай кеширование
if (process.env.NODE_ENV === 'production') {
app.set('view cache', true);
}
// Или явно
app.set('view cache', true);
Компиляция шаблонов
const ejs = require('ejs');
const fs = require('fs');
// Предварительная компиляция
const template = ejs.compile(fs.readFileSync('./views/template.ejs', 'utf8'));
app.get('/fast-route', (req, res) => {
const html = template({ data: 'some data' });
res.send(html);
});
Нестандартные способы использования
Генерация email-шаблонов
const nodemailer = require('nodemailer');
const ejs = require('ejs');
async function sendEmail(to, subject, templateName, data) {
const html = await ejs.renderFile(`./email-templates/${templateName}.ejs`, data);
const transporter = nodemailer.createTransporter({
// настройки SMTP
});
await transporter.sendMail({
from: 'noreply@example.com',
to,
subject,
html
});
}
Генерация статических файлов
const fs = require('fs').promises;
const path = require('path');
async function generateStaticPages() {
const pages = ['about', 'services', 'contact'];
for (const page of pages) {
const html = await ejs.renderFile(`./templates/${page}.ejs`, {
title: `Страница ${page}`,
timestamp: new Date().toISOString()
});
await fs.writeFile(`./dist/${page}.html`, html);
}
}
Автоматизация и CI/CD
EJS отлично подходит для автоматизации. Можно создать скрипт для генерации конфигурационных файлов:
// generate-config.js
const ejs = require('ejs');
const fs = require('fs');
const environments = ['development', 'staging', 'production'];
environments.forEach(env => {
const config = {
environment: env,
database: {
host: process.env[`DB_HOST_${env.toUpperCase()}`] || 'localhost',
port: process.env[`DB_PORT_${env.toUpperCase()}`] || 5432
},
redis: {
host: process.env[`REDIS_HOST_${env.toUpperCase()}`] || 'localhost'
}
};
ejs.renderFile('./templates/nginx.conf.ejs', config, (err, str) => {
if (err) throw err;
fs.writeFileSync(`./configs/nginx-${env}.conf`, str);
});
});
Типичные ошибки и как их избежать
Проблема с XSS
Всегда используй <%= %>
вместо <%- %>
для пользовательского ввода:
// Плохо - уязвимо для XSS
<%- userInput %>
// Хорошо - данные экранируются
<%= userInput %>
Проблема с undefined переменными
// Используй проверки
<% if (typeof user !== 'undefined' && user.name) { %>
<%= user.name %>
<% } %>
// Или значения по умолчанию
<%= user?.name || 'Гость' %>
Интеграция с другими инструментами
Webpack и сборка
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: './src/template.ejs',
templateParameters: {
title: 'Мой сайт',
env: process.env.NODE_ENV
}
})
]
};
Интеграция с Socket.io
const io = require('socket.io')(server);
app.get('/chat', (req, res) => {
res.render('chat', {
title: 'Чат',
user: req.user,
socketUrl: process.env.SOCKET_URL || 'http://localhost:3000'
});
});
Развёртывание на VPS
Для развёртывания EJS-приложения на своём сервере, тебе понадобится надёжный VPS или выделенный сервер. Вот базовая настройка с PM2:
# На сервере
npm install -g pm2
pm2 start server.js --name "my-ejs-app"
pm2 startup
pm2 save
# Nginx конфиг
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Полезные ресурсы
Заключение
EJS — это мощный и гибкий инструмент для создания динамических веб-приложений. Его главные преимущества: простота освоения, близость к обычному HTML и отличная производительность. Если ты только начинаешь работать с Node.js или нужен быстрый результат без сложностей — EJS твой выбор.
Используй EJS когда:
- Нужно быстро создать прототип или MVP
- Команда знает JavaScript, но не хочет изучать новый синтаксис
- Требуется максимальная гибкость в шаблонах
- Важна производительность
Не используй EJS если:
- Нужна строгая логика разделения представления и бизнес-логики
- Работаешь с дизайнерами, которые не знают JavaScript
- Планируешь переход на SPA в будущем
В любом случае, EJS остаётся одним из самых надёжных и проверенных решений для серверного рендеринга в Node.js. Попробуй его в своём следующем проекте — не пожалеешь!
В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.
Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.