🎉

【Tailwind和訳】GETTING STARTED/Optimizing for Production

2021/10/23に公開約9,800字

この記事について

この記事は、GETTING STARTED/Optimizing for Productionの記事を和訳したものです。

記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。

運用時の最適化

本番環境で使われていない CSS を削除することで、最大限のパフォーマンスを得ることができます。

概要

デフォルトの設定では、Tailwind CSS の開発ビルドは、非圧縮で 3566.2kB、Gzipで minify して圧縮すると 289.2kB、Brotliで圧縮すると 71.3kB となります。

非圧縮 Minified Gzip Brotli
3566.2kB 2872.2kB 289.2kB 71.3kB

膨大な量に聞こえるかもしれませんが、開発用のビルドは設計上大きくなっています。

開発の生産性を高めるために、Tailwind は何千ものユーティリティクラスを生成しています。

Tailwind は、巨大なレゴの箱のようなものだと思ってください。床にすべてを捨てて、作りたいものを作り、終わったら使わなかったピースをすべて箱に戻します。

例えば、Tailwind は、スペーシングスケールのすべてのサイズ、マージンを適用したい要素のすべての側面、プロジェクトで使用しているすべてのブレークポイントに対して、マージンユーティリティを生成します。これにより、何百種類もの組み合わせが可能になりますが、これらはすべて利用可能であることが重要ですが、すべてが必要であるとは限りません。

本番環境でビルドする場合は、Tailwind のpurgeオプションを使用して、未使用のスタイルをツリーシェイクし、最終的なビルドサイズを最適化する必要があります。Tailwind で未使用のスタイルを削除した場合、10kb 以上の圧縮された CSS になることは非常に困難です。

パージ可能な HTML の記述

purge機能を使い始める前に、その仕組みを理解し、運用時に重要なスタイルを誤って削除してしまわないように、正しいメンタルモデルを構築することが重要です。

PurgeCSS(ボンネット内で使用しているライブラリ)は、HTML 内のクラスを探す方法として、意図的に非常に素朴なものになっています。HTML を解析してクラス属性を探したり、JavaScript を動的に実行したりすることはなく、単にファイル全体から正規表現にマッチする文字列を探します。

/[^<>"'`\s]*[^<>"'`\s:]/g

これは基本的に、スペース、引用符、角括弧で区切られたあらゆる文字列にマッチするもので、HTML タグ、属性、クラス、さらにはマークアップ内の実際の記述内容も含まれます。

<div class="md:flex">
  <div class="md:flex-shrink-0">
    <img class="rounded-lg md:w-56" src="/img/shopping.jpg" alt="Woman paying for a purchase" />
  </div>
  <div class="mt-4 md:mt-0 md:ml-6">
    <div class="uppercase tracking-wide text-sm text-indigo-600 font-bold">Marketing</div>
    <a
      href="/get-started"
      class="block mt-1 text-lg leading-tight font-semibold text-gray-900 hover:underline"
    >
      Finding customers for your new business
    </a>
    <p class="mt-2 text-gray-600">
      Getting a new business off the ground is a lot of hard work. Here are five ideas you can use
      to find your first customers.
    </p>
  </div>
</div>

つまり,テンプレート内のクラス文字列を文字列連結で動的に作らないようにすることが重要です。そうしないと,PurgeCSS はそれらのクラスを保存することができません。

クラス名の作成に文字列の連結を使用しない

<div class="text-{{  error  ?  'red'  :  'green'  }}-600"></div>

完全なクラス名を動的に選択する

<div class="{{  error  ?  'text-red-600'  :  'text-green-600'  }}"></div>

クラス名がテンプレートにそのまま表示されている限り,PurgeCSS はそれを削除しません。

未使用の CSS を削除する

基本的な使い方

まず、purgeオプションを使って、すべてのテンプレートファイルのパスの配列を指定します。

tailwind.config.js
module.exports = {
  purge: [
    './src/**/*.html',
    './src/**/*.vue',
    './src/**/*.jsx',
  ],
  theme: {},
  variants: {},
  plugins: [],
}

このリストには、プロジェクト内でスタイルを名前で参照しているすべてのファイルを含める必要があります。例えば、プロジェクト内に HTML 内のいくつかのクラスを動的に切り替える JS ファイルがある場合、そのファイルがこのリストに含まれていることを確認する必要があります。

これで、NODE_ENVproductionに設定して CSS をコンパイルすると、Tailwind は自動的に CSS から未使用のスタイルを削除します。

手動で有効にする

未使用のスタイルが削除されるかどうかを(環境変数に暗黙的に依存するのではなく)手動で制御したい場合は、オブジェクト構文を使用してenabledオプションを指定し、contentオプションでテンプレートを指定します。

tailwind.config.js
module.exports = {
  purge: {
    enabled: true,
    content: ['./src/**/*.html'],
  },
  // ...
}

開発中に未使用のスタイルを削除すると、テンプレートを変更するたびに再コンパイルする必要があり、PurgeCSS を有効にしてコンパイルすると、非常に時間がかかるため、本番環境では未使用のスタイルのみを削除することをお勧めします。

特定のクラスのセーフリスト化

特定のクラスをセーフリストに登録して、CSS から誤って削除されないようにする必要がある場合 (データベースなどから取得したコンテンツで使用されている場合など)、トップレベルのsafelistオプションを使用できます。

tailwind.config.js
module.exports = {
  purge: {
    content: ['./src/**/*.html'],
    safelist: [
      'bg-blue-500',
      'text-center',
      'hover:opacity-100',
      // ...
      'lg:text-right',
    ]
  },
  // ...
}

コンテンツの変換

時には、HTML にコンパイルされるフォーマットでコンテンツをオーサリングしていて、潜在的なクラスを探す前にそのコンテンツを HTML にコンパイルした方が良い場合があります。このような例としては、マークダウンファイルでの作業が挙げられます。

transformオプションを使用すると、クラスを検索する前に、特定の拡張子にマッチするファイルをトランスフォームするよう Tailwind に指示することができ、最も正確な結果を保証します。

tailwind.config.js
let remark = require('remark')
module.exports = {
  purge: {
    content: ['./src/**/*.{html,md}'],
    transform: {
      md: (content) => {
        return remark().process(content)
      }
    }
  },
  // ...
}

抽出ロジックのカスタマイズ

特定のファイルタイプのコンテンツ内のクラスを検出するために Tailwind が使用するロジックをオーバーライドする必要がある場合、extractオプションを使用して、一致するファイル内の潜在的なクラスを検出するために使用される関数を提供することができます。

tailwind.config.js
module.exports = {
  purge: {
    content: ['./src/**/*.{html,md}'],
    extract: {
      md: (content) => {
        return content.match(/[^<>"'`\s]*/)
      }
    }
  },
  // ...
}

これは高度な機能であり、ほとんどのユーザーには必要ありません。Tailwind のデフォルトの抽出ロジックは、ほとんどすべてのプロジェクトで非常にうまく機能しています。

HTML 要素の保存

Tailwind はデフォルトで、htmlbodyph1などの基本的な HTML 要素のスタイルをすべて CSS に保持します。これは、例えばマークダウンのソースファイル(実際にはh1タグが存在しない)を使用している場合や、(Next.js のように)ドキュメントシェル(htmlbodyタグを含む)をベンダーディレクトリのどこかに隠すフレームワークを使用している場合に、誤ってオーバーパージしてしまうことを最小限に抑えるためです。

この動作を無効にしたい場合は、preserveHtmlElementsを false に設定してください。

tailwind.config.js
module.exports = {
  purge: {
    preserveHtmlElements: false,
    content: ['./src/**/*.html'],
  },
  // ...
}

私たちは一般的にこれを推奨しませんし、リスクがメリットを上回ると考えていますが、私たちは警察ではありませんので、使用を止めることもありません。

特定のレイヤーのパージ

デフォルトでは、Tailwind はbasecomponentsutilitiesの各レイヤーにあるすべてのスタイルを消去します。これを変更したい場合は、layersオプションを使用して、パージしたいレイヤーを手動で指定してください。

tailwind.config.js
module.exports = {
  purge: {
    layers: ['components', 'utilities'],
    content: ['./src/**/*.html'],
  },
  // ...
}

すべての未使用のスタイルを削除する

デフォルトでは、Tailwind は自分自身で生成した未使用のクラス、または@layerディレクティブで明示的にラップされたクラスのみを削除します。例えばデータピッカーライブラリのように、プロジェクトに取り込んだサードパーティの CSS から未使用のスタイルを削除することはありません。

/* これらのスタイルはすべてパージされます。 */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* これらのスタイルはパージされます。 */
@layer components {
  .btn { /* ... */ }
}
/* これらのスタイルはパージされません。 */
.flatpickr-innerContainer { /* ... */ }
.flatpickr-weekdayContainer { /* ... */ }
.flatpickr-weekday { /* ... */ }

これは、node_modulesフォルダの奥深くで他の依存関係の一部として参照されているだけのクラスのように、必要かもしれないがテンプレートで直接参照されていないスタイルを誤って削除してしまうことを避けるためです。

本当に使われていないスタイルをすべて削除したい場合は、mode: 'all'preserveHtmlElements: falseを設定し、クラスや HTML 要素を参照している可能性のあるすべてのファイルのパスを指定するように注意してください

tailwind.config.js
module.exports = {
  purge: {
    mode: 'all',
    preserveHtmlElements: false,
    content: [
      './src/**/*.js',
      './node_modules/flatpickr/**/*.js',
    ],
  },
  // ...
}

この方法はお勧めできません。一般的には、より保守的なデフォルトのアプローチを採用することで、ファイルサイズの 99%の利点を得ることができます。

未使用のキーフレームの削除

PurgeCSS は,デフォルトでは未使用の@keyframesルールを削除しませんので,アニメーション関連のスタイルを使用していなくても,スタイルシートに残ってしまうことがあります。これらは,PurgeCSS のoptionsにあるkeyframesオプションで削除できます。

tailwind.config.js
module.exports = {
  purge: {
    content: ['./src/**/*.html'],
    options: {
      keyframes: true,
    },
  },
  // ...
}

PurgeCSS オプション

他のオプションを PurgeCSS に直接渡す必要がある場合は,optionsを使って渡すことができます。

tailwind.config.js
module.exports = {
  purge: {
    content: ['./src/**/*.html'],
    // これらのオプションは、PurgeCSSに直接渡されます。
    options: {
      safelist: ['bg-red-500', 'px-4'],
      blocklist: [/^debug-/],
      keyframes: true,
      fontFace: true,
    },
  },
  // ...
}

利用可能なオプションの一覧は,PurgeCSS のドキュメントに記載されています。

別のアプローチ

何らかの理由で PurgeCSS を使用できない場合は、設定ファイルから未使用の値を削除することで Tailwind のフットプリントを減らすこともできます。

デフォルトのテーマでは、色、ブレークポイント、サイズ、マージンなどが豊富に用意されており、Tailwind を使って何かを試作したり、CodePen のデモを作成したり、ワークフローを試したりする際に、できるだけ楽しく、スムーズに作業ができるようになっています。

パディングヘルパーが十分に用意されていなかったからといって、新たに CSS を書かなければならなかったり、デモでオレンジ色を使いたかったのに青色しか用意されていなかったからといって、新たに CSS を書かなければならないようなことは避けたいのです。

しかし、これにはトレードオフがあります。デフォルトのビルドは、目的に応じた設定ファイルを持つプロジェクトに比べて、かなり重くなっています。

ここでは、生成される CSS を小さくしてパフォーマンスを維持するための戦略をいくつかご紹介します。

カラーパレットの制限

デフォルトのテーマには、背景、ボーダー、テキスト、プレースホルダーに使用されるカラーがなんと84 色も含まれています。これらのカラーには、hover:focus:のバリエーションがあり、デフォルトの 6 つのスクリーンサイズではレスポンシブなバリエーションも用意されています。

デフォルトでは、これらの色に対応するために何千ものクラスが生成され、最終的なビルドサイズの半分近くを占めています。

実際には、これほど多くの色を必要とするプロジェクトはほとんどなく、必要のない色を削除することで、全体のファイルサイズに大きな影響を与えることができます。

ここでは、カラーパレットを小さくすることで、最終的なサイズにどのような影響があるのかを紹介します。

オリジナル Minified Gzip Brotli
84 (デフォルト) 3566.2kB 2872.2kB 289.2kB 71.3kB
50 2726.9kB 2167.5kB 231.9kB 57.2kB
25 2098.6kB 1639.0kB 189.9kB 47.1kB

使用しないブレークポイントの削除

ほぼすべての Tailwind ユーティリティは、すべてのスクリーンサイズに対応してコピーされているため、スクリーンサイズを少なくすることで、全体のファイルサイズにも大きな影響を与えます。

ここでは、少ないスクリーンを定義することで、出力にどのような影響があるかを説明します。

オリジナル Minified Gzip Brotli
5 (デフォルト) 3566.2kB 2872.2kB 289.2kB 71.3kB
3 2343.9kB 1894.0kB 192.8kB 60.9kB
3 1742.7kB 1414.7kB 145.0kB 55.3kB
1 1141.7kB 935.6kB 96.8kB 50.7kB

3 つのスクリーンサイズと 35 色だけが必要な場合は、何も変更せずに 44.3kB に圧縮されます。

未使用のコアプラグインとバリアントの無効化

プロジェクトで必要としないユーティリティー・プラグインは、設定ファイルのcorePluginsセクションでfalseに設定することで完全に無効にすることができます。

tailwind.config.js
module.exports = {
  // ...
  corePlugins: {
    float: false
  }
}

一握りのユーティリティーしか必要としない場合は、残しておきたいユーティリティープラグインを配列にしてcorePluginsに渡すことができます。

tailwind.config.js
module.exports = {
  // ...
  corePlugins: [
    'margin',
    'padding'
  ]
}

上記は、マージンとパディング以外のすべてのユーティリティを無効にします。

ユーティリティーは必要だが、レスポンシブバージョンは必要ないという場合は、そのバリアントを空の配列に設定することで、83%少ないクラスを生成することができます。

module.exports = {
  // ...
  variants: {
    appearance: [],
  },
}

これらは、カラーパレットの制限やブレイクポイントの数を減らすことに比べれば小さな成果ですが、それでも積み重ねることができます。

Discussion

ログインするとコメントできます