Home » Как использовать EJS для шаблонизации Node.js приложений
Как использовать EJS для шаблонизации Node.js приложений

Как использовать 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. Попробуй его в своём следующем проекте — не пожалеешь!


В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.

Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.

Leave a reply

Your email address will not be published. Required fields are marked