Skip to content
27 / 36

Как Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ сТатиС ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ² (gzip, Brotli)?


ΠŸΠΎΠ»Π½Ρ‹ΠΉ ΠΎΡ‚Π²Π΅Ρ‚

ΠŸΡ€ΠΎΡ†Π΅ΡΡ согласования сТатия:

JavaScript
Π‘Ρ€Π°ΡƒΠ·Π΅Ρ€                                    Π‘Π΅Ρ€Π²Π΅Ρ€
|  |
| -- GET /app.js |
| Accept-Encoding: gzip, deflate, br  --> |
|  |
| [Π‘Π΅Ρ€Π²Π΅Ρ€ Π²Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ Π»ΡƒΡ‡ΡˆΠΈΠΉ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ] |
|  |
| <-- 200 OK |
| Content-Encoding: br |
| Content-Type: application/javascript |
| [сТатоС Ρ‚Π΅Π»ΠΎ ΠΎΡ‚Π²Π΅Ρ‚Π°] |
|  |
| [Π‘Ρ€Π°ΡƒΠ·Π΅Ρ€ распаковываСт автоматичСски] |

Настройка Π½Π° nginx:

Nginx

# nginx.conf

# gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;           # 1-9, баланс скорости ΠΈ стСпСни сТатия
gzip_min_length 256;         # Π½Π΅ ΡΠΆΠΈΠΌΠ°Ρ‚ΡŒ ΠΌΠ΅Π»ΠΊΠΈΠ΅ ΠΎΡ‚Π²Π΅Ρ‚Ρ‹
gzip_types
  text/plain
  text/css
  text/javascript
  application/javascript
  application/json
  application/xml
  image/svg+xml
  font/woff2;

# Brotli (Π½ΡƒΠΆΠ΅Π½ ΠΌΠΎΠ΄ΡƒΠ»ΡŒ ngx_brotli)
brotli on;
brotli_comp_level 6;         # 1-11, для on-the-fly ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ 4-6
brotli_types
  text/plain
  text/css
  text/javascript
  application/javascript
  application/json
  application/xml
  image/svg+xml;

ΠŸΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ сТатиС (статичСскиС Ρ„Π°ΠΉΠ»Ρ‹):

Bash

# Π‘ΠΆΠ°Ρ‚ΠΈΠ΅ ΠΏΡ€ΠΈ сборкС -- ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ, Π½Π΅ Ρ‚Ρ€Π°Ρ‚ΠΈΡ‚ CPU Π½Π° Π»Π΅Ρ‚Ρƒ

# gzip
gzip -k -9 dist/app.js        # создаёт app.js.gz рядом с ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΠΎΠΌ

# Brotli (ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ 11 -- ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ, ΠΌΠ΅Π΄Π»Π΅Π½Π½Ρ‹ΠΉ, Π½ΠΎ Π»ΡƒΡ‡ΡˆΠ΅Π΅ сТатиС)
brotli -k -q 11 dist/app.js   # создаёт app.js.br

Nginx

# nginx Ρ€Π°Π·Π΄Π°Ρ‘Ρ‚ прСдсТатыС Ρ„Π°ΠΉΠ»Ρ‹
gzip_static on;      # ΠΈΡ‰Π΅Ρ‚ .gz рядом с Ρ„Π°ΠΉΠ»ΠΎΠΌ
brotli_static on;    # ΠΈΡ‰Π΅Ρ‚ .br рядом с Ρ„Π°ΠΉΠ»ΠΎΠΌ

Настройка Π² Express:

JavaScript
import express from 'express';
import compression from 'compression';

const app = express();

// Π‘Π°Π·ΠΎΠ²ΠΎΠ΅ сТатиС (gzip ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ)
app.use(compression({
      level: 6,
      threshold: 1024,  // Π½Π΅ ΡΠΆΠΈΠΌΠ°Ρ‚ΡŒ ΠΎΡ‚Π²Π΅Ρ‚Ρ‹ мСньшС 1 KB
      filter: (req, res) => {

        // НС ΡΠΆΠΈΠΌΠ°Ρ‚ΡŒ ΡƒΠΆΠ΅ сТатыС Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Ρ‹
        const contentType = res.getHeader('Content-Type') || '';
        if (/image| video |audio/.test(contentType)) return false;
        return compression.filter(req, res);
      },
    }));

// Для Brotli Π² Express Π½ΡƒΠΆΠ΅Π½ shrink-ray-current ΠΈΠ»ΠΈ iltorb
import shrinkRay from 'shrink-ray-current';

app.use(shrinkRay({
      brotli: { quality: 4 }, // Π½Π° Π»Π΅Ρ‚Ρƒ -- Π½ΠΈΠΆΠ΅ ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ для скорости
    }));

Π‘Ρ€Π°Π²Π½Π΅Π½ΠΈΠ΅ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠ²:

JavaScript
Π€Π°ΠΉΠ»: React bundle (500 KB)
-----------------------------------------
Π‘Π΅Π· сТатия:  500 KB  (100%)
gzip -6:     145 KB  (29%)    быстро
gzip -9:     140 KB  (28%)    Ρ‡ΡƒΡ‚ΡŒ ΠΌΠ΅Π΄Π»Π΅Π½Π½Π΅Π΅
Brotli -4:   130 KB  (26%)    сравнимо с gzip ΠΏΠΎ скорости
Brotli -11:  115 KB  (23%)    ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎ, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для статики

πŸ“Œ Π Π΅Π°Π»ΡŒΠ½Ρ‹Π΅ кСйсы

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° сТатия Π² ΠΊΠΎΠ΄Π΅:

JavaScript
// ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ, сТимаСтся Π»ΠΈ рСсурс
const resources = performance.getEntriesByType('resource');

for (const res of resources) {
  if (res.decodedBodySize > 0 && res.transferSize > 0) {
    const ratio = 1 - res.encodedBodySize / res.decodedBodySize;
    const isCompressed = ratio > 0.1; // сТатиС большС 10%

    if (!isCompressed && res.decodedBodySize > 10_000) {
      console.warn(
        `НСсТатый рСсурс (${(res.decodedBodySize / 1024).toFixed(0)} KB): ${res.name}`
      );
    }
  }
}

Webpack-ΠΏΠ»Π°Π³ΠΈΠ½ для прСдсТатия:

JavaScript
// webpack.config.js
import CompressionPlugin from 'compression-webpack-plugin';
import zlib from 'zlib';

export default {
  plugins: [
    // gzip
    new CompressionPlugin({
        filename: '[path][base].gz',
        algorithm: 'gzip',
        test: /\.(js| css |html| svg |json)$/,
        threshold: 1024,
        minRatio: 0.8,
      }),

    // Brotli
    new CompressionPlugin({
        filename: '[path][base].br',
        algorithm: 'brotliCompress',
        test: /\.(js| css |html| svg |json)$/,
        compressionOptions: { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 } },
        threshold: 1024,
        minRatio: 0.8,
      }),
  ],
};

РСзюмС

Π‘ΠΆΠ°Ρ‚ΠΈΠ΅ -- ΠΎΠ΄Π½Π° ΠΈΠ· самых простых ΠΈ эффСктивных ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΉ. Brotli сТимаСт Π»ΡƒΡ‡ΡˆΠ΅ gzip Π½Π° 15-25% для тСкста. Для статичСских Ρ„Π°ΠΉΠ»ΠΎΠ² ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ сТатиС Π½Π° максимальном ΡƒΡ€ΠΎΠ²Π½Π΅ (Brotli 11), для динамичСских -- сТатиС Π½Π° Π»Π΅Ρ‚Ρƒ с ΡƒΠΌΠ΅Ρ€Π΅Π½Π½Ρ‹ΠΌ ΡƒΡ€ΠΎΠ²Π½Π΅ΠΌ (Brotli 4-6). НС сТимай изобраТСния, Π²ΠΈΠ΄Π΅ΠΎ ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ Π±ΠΈΠ½Π°Ρ€Π½Ρ‹Π΅ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Ρ‹ -- ΠΎΠ½ΠΈ ΡƒΠΆΠ΅ сТаты.

Как Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ сТатиС ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ² (gzip, Brotli)? | JScriptiser