Compression для JavaScript (Node.js): полное руководство с примерами
1. Что это и зачем нужна
Библиотека compression для Node.js — это middleware (промежуточное ПО) для фреймворка Express (и других совместимых фреймворков), которое автоматически сжимает HTTP-ответы сервера с помощью алгоритмов Gzip, Deflate или Brotli. Она является стандартом де-факто для оптимизации производительности веб-приложений на Node.js, так как позволяет значительно уменьшить размер передаваемых данных (HTML, CSS, JavaScript, JSON, изображения и т.д.) между сервером и клиентом.
Зачем это нужно? В современном вебе скорость загрузки страниц критически важна для пользовательского опыта и SEO. Сжатие данных позволяет уменьшить объем трафика в 3–10 раз (в зависимости от типа контента). Например, HTML-файл размером 100 КБ после Gzip-сжатия может весить всего 10–15 КБ. Это особенно актуально для мобильных пользователей с ограниченным трафиком и для API, возвращающих большие JSON-объекты. Без использования compression сервер будет отдавать "сырые" данные, что увеличивает время загрузки и нагрузку на сеть.
Библиотека полностью автоматизирует процесс: она проверяет заголовок Accept-Encoding в запросе клиента, выбирает подходящий алгоритм сжатия (обычно gzip или brotli, если поддерживается), сжимает ответ и добавляет соответствующие заголовки (Content-Encoding, Vary). Разработчику нужно лишь подключить middleware в цепочку обработки запросов Express. Это делает compression незаменимым инструментом для любого production-приложения на Node.js.
2. Установка
Библиотека устанавливается через npm (Node Package Manager). Убедитесь, что у вас установлен Node.js (версия 12 или выше). Выполните команду в корне вашего проекта:
npm install compression
Если вы используете yarn:
yarn add compression
После установки библиотека будет доступна для импорта в вашем коде.
3. Быстрый старт — минимальный рабочий пример
Самый простой способ начать использовать compression — подключить его как middleware в Express. Ниже приведен минимальный сервер, который сжимает все ответы:
const express = require('express');
const compression = require('compression');
const app = express();
const port = 3000;
// Подключаем middleware сжатия
app.use(compression());
// Обычный маршрут
app.get('/', (req, res) => {
// Отправляем большой ответ (повторяем строку 1000 раз)
const bigData = 'Hello, World! '.repeat(1000);
res.send(bigData);
});
app.listen(port, () => {
console.log(`Сервер запущен на http://localhost:${port}`);
});
После запуска (node app.js) откройте браузер на http://localhost:3000. В инструментах разработчика (вкладка Network) вы увидите, что ответ сжат (заголовок Content-Encoding: gzip). Размер ответа будет в разы меньше исходного.
4. Основные методы/функции/классы
Библиотека compression предоставляет одну основную функцию — middleware, которая настраивается через объект опций. Рассмотрим 8 ключевых аспектов работы с ней.
4.1. compression([options]) — создание middleware
Сигнатура: compression(options?: CompressionOptions): RequestHandler
Что делает: Создает и возвращает функцию middleware для Express. Принимает необязательный объект с настройками.
Пример:
const compression = require('compression');
const app = require('express')();
// Без опций (стандартное поведение)
app.use(compression());
// С опциями
app.use(compression({
level: 6, // Уровень сжатия (0-9)
threshold: 1024, // Не сжимать ответы меньше 1 КБ
filter: (req, res) => {
// Собственная логика фильтрации
return !req.headers['x-no-compression'];
}
}));
4.2. Опция level — уровень сжатия
Сигнатура: level: number (от 0 до 9)
Что делает: Определяет степень сжатия. 0 — без сжатия (только упаковка), 1 — быстрое, но слабое сжатие, 9 — максимальное сжатие (медленнее). По умолчанию: -1 (означает стандартный уровень zlib, обычно 6).
Пример:
app.use(compression({ level: 9 })); // Максимальное сжатие, но больше нагрузка на CPU
4.3. Опция threshold — порог сжатия
Сигнатура: threshold: number | string (в байтах)
Что делает: Устанавливает минимальный размер ответа (в байтах), при котором применяется сжатие. Ответы меньше этого порога не сжимаются. По умолчанию: 1024 (1 КБ). Можно указать строку, например '1kb'.
Пример:
app.use(compression({ threshold: 0 })); // Сжимать все ответы, даже маленькие
app.use(compression({ threshold: '5kb' })); // Сжимать только ответы больше 5 КБ
4.4. Опция filter — фильтр для выборочного сжатия
Сигнатура: filter: (req: Request, res: Response) => boolean
Что делает: Позволяет задать функцию, которая решает, нужно ли сжимать конкретный ответ. Функция получает объекты запроса и ответа. Должна вернуть true (сжимать) или false (не сжимать). По умолчанию используется встроенная функция, которая проверяет Content-Type и не сжимает уже сжатые форматы (изображения, видео).
Пример:
app.use(compression({
filter: (req, res) => {
// Не сжимаем ответы для определенных путей
if (req.path.startsWith('/api/stream')) return false;
// Используем стандартную логику для остальных
return compression.filter(req, res);
}
}));
4.5. Опция chunkSize — размер чанка
Сигнатура: chunkSize: number (в байтах)
Что делает: Определяет размер внутреннего буфера (чанка) для сжатия. По умолчанию: 16384 (16 КБ). Влияет на использование памяти и скорость. Увеличение может улучшить сжатие, но требует больше памяти.
Пример:
app.use(compression({ chunkSize: 32 * 1024 })); // 32 КБ
4.6. Опция memLevel — уровень использования памяти
Сигнатура: memLevel: number (от 1 до 9)
Что делает: Определяет, сколько памяти выделяется для внутренних структур сжатия. 1 — минимум памяти (медленнее), 9 — максимум памяти (быстрее). По умолчанию: 8.
Пример:
app.use(compression({ memLevel: 9 })); // Максимальная скорость, больше памяти
4.7. Опция strategy — стратегия сжатия
Сигнатура: strategy: number (константы zlib)
Что делает: Определяет алгоритм сжатия. Возможные значения: zlib.constants.Z_DEFAULT_STRATEGY (0), Z_FILTERED (1), Z_HUFFMAN_ONLY (2), Z_RLE (3), Z_FIXED (4). По умолчанию: Z_DEFAULT_STRATEGY. Для данных, которые уже частично сжаты (например, PNG), может быть полезна стратегия Z_FILTERED.
Пример:
const zlib = require('zlib');
app.use(compression({
strategy: zlib.constants.Z_FILTERED
}));
4.8. Опция windowBits — размер окна сжатия
Сигнатура: windowBits: number
Что делает: Определяет размер окна (истории) для алгоритма Deflate. Значение от 8 до 15. По умолчанию: 15 (максимальное окно, лучшее сжатие). Меньшие значения уменьшают использование памяти, но снижают степень сжатия.
Пример:
app.use(compression({ windowBits: 12 })); // Меньше памяти, хуже сжатие
5. Реальный практический пример (100-200 строк кода)
Ниже представлен полноценный сервер на Express с использованием compression, который включает логирование, обработку ошибок, статические файлы и API. Пример демонстрирует типичную production-конфигурацию.
const express = require('express');
const compression = require('compression');
const path = require('path');
const fs = require('fs');
const app = express();
const PORT = process.env.PORT || 3000;
// 1. Настройка compression с кастомными опциями
app.use(compression({
level: 6, // Хороший баланс скорость/сжатие
threshold: 256, // Сжимаем ответы от 256 байт
filter: (req, res) => {
// Не сжимаем уже сжатые форматы и Server-Sent Events
if (req.headers['accept'] === 'text/event-stream') return false;
// Используем стандартный фильтр для остального
return compression.filter(req, res);
}
}));
// 2. Middleware для логирования запросов
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
const size = res.get('Content-Length') || 'chunked';
const encoding = res.get('Content-Encoding') || 'none';
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} - ${res.statusCode} (${size} bytes, encoding: ${encoding}, ${duration}ms)`);
});
next();
});
// 3. Раздача статических файлов (сжатие будет применено автоматически)
app.use(express.static(path.join(__dirname, 'public'), {
maxAge: '1d', // Кеширование на 1 день
etag: true,
lastModified: true
}));
// 4. API эндпоинт: возвращает большой JSON
app.get('/api/data', (req, res) => {
// Генерируем массив из 1000 объектов
const largeData = Array.from({ length: 1000 }, (_, i) => ({
id: i,
name: `User ${i}`,
email: `user${i}@example.com`,
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '.repeat(10)
}));
res.json(largeData);
});
// 5. API эндпоинт: возвращает HTML-страницу
app.get('/report', (req, res) => {
const html = `
Отчет
Большой отчет
${'Текст отчета. '.repeat(500)}
`;
res.type('html').send(html);
});
// 6. Эндпоинт для скачивания файла (сжатие будет применено)
app.get('/download', (req, res) => {
const filePath = path.join(__dirname, 'data.txt');
// Создаем большой файл, если его нет
if (!fs.existsSync(filePath)) {
fs.writeFileSync(filePath, 'A'.repeat(1024 * 100)); // 100 КБ
}
res.download(filePath);
});
// 7. Обработка ошибок
app.use((err, req, res, next) => {
console.error('Ошибка:', err);
res.status(500).json({ error: 'Внутренняя ошибка сервера' });
});
// 8. Запуск сервера
app.listen(PORT, () => {
console.log(`Сервер запущен на http://localhost:${PORT}`);
console.log('Тестовые эндпоинты:');
console.log(' GET /api/data - большой JSON');
console.log(' GET /report - большая HTML страница');
console.log(' GET /download - скачивание файла');
});
Этот пример показывает, как compression интегрируется в реальное приложение: он автоматически сжимает JSON, HTML, статические файлы и даже файлы для скачивания. Логирование помогает отследить, какие ответы были сжаты и насколько эффективно.
6. Советы по производительности и типичные ошибки
Советы по производительности
- Не используйте максимальный уровень сжатия (9) для динамических ответов. Уровень 6 (по умолчанию) дает отличный баланс между степенью сжатия и временем обработки. Уровень 9 может увеличить загрузку CPU на 50-100% при незначительном выигрыше в размере (1-3%).
- Настройте порог (threshold) разумно. Сжатие маленьких ответов (менее 1 КБ) неэффективно — накладные расходы на заголовки и алгоритм могут превысить выгоду. Оставьте значение по умолчанию 1024 байта или уве