brotli を使用してネットワーク ペイロードを最小化、圧縮する

Michael DiBlasio
Michael DiBlasio

この Codelab は、ネットワーク ペイロードを圧縮して圧縮する Codelab の拡張版であり、圧縮の基本コンセプトに精通していることを前提としています。この Codelab では、gzip などの他の圧縮アルゴリズムと比較して、Brotli 圧縮(br)で圧縮率とアプリの全体的なサイズをさらに削減する方法について説明します。

アプリのスクリーンショット

測定

最適化を追加する前に、まずアプリケーションの現在の状態を分析することをおすすめします。

  1. [Remix to Edit] をクリックして、プロジェクトを編集可能にします。
  2. サイトをプレビューするには、[アプリを表示] を押してから、[全画面表示] 全画面表示 を押します。

前の ネットワーク ペイロードの圧縮と圧縮に関する Codelab では、main.js のサイズを 225 KB から 61.6 KB に削減しました。この Codelab では、Brotli 圧縮によってこのバンドルサイズをさらに削減する方法について説明します。

Brotli 圧縮

Brotli は、gzip よりも優れたテキスト圧縮結果を提供する新しい圧縮アルゴリズムです。CertSimple によると、Brotli のパフォーマンスは次のとおりです。

  • JavaScript の gzip より 14% 小さい
  • HTML の gzip より 21% 小さい
  • CSS の gzip より 17% 小さい

Brotli を使用するには、サーバーが HTTPS をサポートしている必要があります。Brotli はすべての最新ブラウザでサポートされています。Brotli をサポートするブラウザでは、Accept-Encoding ヘッダーに br が含まれます。

Accept-Encoding: gzip, deflate, br

使用されている圧縮アルゴリズムは、Chrome デベロッパー ツールの [Network] タブ(Command+Option+I または Ctrl+Alt+I)の Content-Encoding フィールドで確認できます。

ネットワーク パネル。[Content-encoding] 列には、gzip や brotli(br)など、さまざまなアセットに使用されているエンコードが表示されます。

Brotli を有効にする方法

Brotli でエンコードされたリソースを送信するようにウェブサーバーを設定する方法は、リソースをエンコードする方法によって異なります。リクエスト時に Brotli でリソースを動的に圧縮する(動的)か、事前にエンコードしてユーザーがリクエストするときにすでに圧縮されているようにする(静的)かのいずれかを選択できます。

動的圧縮

動的圧縮では、ブラウザからリクエストされたときにアセットをオンザフライで圧縮します。

メリット

  • 保存された圧縮バージョンのアセットの作成と更新は必要ありません。
  • オンザフライの圧縮は、動的に生成されるウェブページに特に適しています。

デメリット

  • 圧縮率を高めるためにファイルの圧縮レベルを上げると、圧縮に時間がかかります。これにより、サーバーがアセットを送信する前にアセットが圧縮されるのをユーザーが待機するため、パフォーマンスが低下する可能性があります。

Node と Express を使用した動的圧縮

server.js ファイルは、アプリケーションをホストする Node サーバーの設定を担当します。

const express = require('express');
const app = express();
app.use(express.static('public'));

const listener = app.listen(process.env.PORT, function() {
  console.log(`Your app is listening on port ${listener.address().port}`);
});

express をインポートし、express.static ミドルウェアを使用して public/directory 内のすべての静的 HTML、JS、CSS ファイルを読み込むだけです(これらのファイルは、ビルドごとに webpack によって作成されます)。

すべてのアセットがリクエストされるたびに brotli を使用して圧縮されるようにするには、shrink-ray モジュールを使用します。まず、package.jsondevDependency として追加します。

"devDependencies": {
  // ...
  "shrink-ray": "^0.1.3"
},

サーバー ファイル server.js にインポートします。

const express = require('express');
const shrinkRay = require('shrink-ray');

express.static がマウントされる前に、ミドルウェアとして追加します。

// ...
const app = express();

// Compress all requests
app.use(shrinkRay());
app.use(express.static('public'));

アプリを再読み込みし、[ネットワーク] パネルでバンドルのサイズを確認します。

動的 Brotli 圧縮を使用したバンドルのサイズ。

Content-Encoding ヘッダーで、bz から brotli が適用されていることがわかります。main.bundle.js225 KB から 53.1 KB に削減されました。これは、gzip(61.6 KB)と比較して約 14% 小さくなります。

静的圧縮

静的圧縮の考え方は、アセットを事前に圧縮して保存することです。

メリット

  • 高い圧縮レベルによるレイテンシの問題は解消されます。ファイルは直接取得できるため、ファイルの圧縮のためにオンザフライで何もする必要はありません。

デメリット

  • アセットはビルドごとに圧縮する必要があります。高い圧縮レベルを使用すると、ビルド時間が大幅に長くなる可能性があります。

Node と Express による静的圧縮(webpack を使用)

静的圧縮ではファイルを事前に圧縮するため、ビルドステップの一部としてアセットを圧縮するように webpack の設定を変更できます。これには brotli-webpack-plugin を使用できます。

まず、package.jsondevDependency として追加します。

"devDependencies": {
  // ...
 "brotli-webpack-plugin": "^1.1.0"
},

他の webpack プラグインと同様に、構成ファイル webpack.config.js にインポートします。

var path = require("path");

//...
var BrotliPlugin = require('brotli-webpack-plugin');

これを plugins 配列に含めます。

module.exports = {
  // ...
  plugins: [
    // ...
    new BrotliPlugin({
      asset: '[file].br',
      test: /\.(js)$/
    })
  ]
},

プラグイン アレイでは、次の引数を使用します。

  • asset: ターゲット アセット名。
  • [file] は、元のアセット ファイル名に置き換えます。
  • test: この正規表現に一致するすべてのアセット(.js で終わる JavaScript アセット)が処理されます。

たとえば、main.jsmain.js.br に変更されます。

アプリが再読み込みされて再ビルドされると、メイン バンドルの圧縮バージョンが作成されます。Glitch Console を開き、Node サーバーが提供する最終的な public/ ディレクトリの内容を確認します。

  1. [ツール] ボタンをクリックします。
  2. [コンソール] ボタンをクリックします。
  3. コンソールで次のコマンドを実行して、public ディレクトリに移動し、そのディレクトリ内のすべてのファイルを表示します。
cd public
ls -lh
静的 Brotli 圧縮を使用したバンドルのサイズ

バンドルの Brotli 圧縮バージョン main.bundle.js.br もここに保存されるようになりました。main.bundle.js と比較してサイズが約 76% 小さく(225 KB 対 53 KB)なります。

次に、元の JS バージョンがリクエストされたときに、これらの Brotli 圧縮ファイルを送信するようにサーバーに指示します。これを行うには、express.static でファイルが提供される前に、server.js で新しいルートを定義します。

const express = require('express');
const app = express();

app.get('*.js', (req, res, next) => {
  req.url = req.url + '.br';
  res.set('Content-Encoding', 'br');
  res.set('Content-Type', 'application/javascript; charset=UTF-8');
  next();
});

app.use(express.static('public'));

app.get は、特定のエンドポイントの GET リクエストに応答する方法をサーバーに指示するために使用されます。コールバック関数を使用して、このリクエストの処理方法を定義します。このルートの仕組みは次のとおりです。

  • 最初の引数として '*.js' を指定すると、JS ファイルを取得するために呼び出されるすべてのエンドポイントで機能します。
  • コールバック内で、.br がリクエストの URL に添付され、Content-Encoding レスポンス ヘッダーが br に設定されます。
  • Content-Type ヘッダーは application/javascript; charset=UTF-8 に設定され、MIME タイプが指定されます。
  • 最後に、next() は、次のコールバックにシーケンスが続行されるようにします。

一部のブラウザでは Brotli 圧縮がサポートされていない可能性があるため、Brotli 圧縮されたファイルを返す前に、Accept-Encoding リクエスト ヘッダーに br が含まれていることを確認して、Brotli がサポートされていることを確認します。

const express = require('express');
const app = express();

app.get('*.js', (req, res, next) => {
  if (req.header('Accept-Encoding').includes('br')) {
    req.url = req.url + '.br';
    console.log(req.header('Accept-Encoding'));
    res.set('Content-Encoding', 'br');
    res.set('Content-Type', 'application/javascript; charset=UTF-8');
  }

  next();
});

app.use(express.static('public'));

アプリが再読み込みされたら、[ネットワーク] パネルをもう一度確認します。

バンドルサイズが 53.1 KB(225 KB から)に縮小

完了しました。Brotli 圧縮を使用してアセットをさらに圧縮しました。

まとめ

このコードラボでは、brotli を使用してアプリの全体的なサイズをさらに削減する方法について説明しました。サポートされている場合、brotligzip よりも強力な圧縮アルゴリズムです。