「及時」模式:全新世代的 Tailwind CSS
Tailwind CSS on GitHub

優化生產模式

在生產模式 (production) 的架構下排除未使用的 CSS 以達到最佳性能。

Overview

在預設配置下的開發者環境架構中, Tailwind CSS 未壓縮的檔案大小為 3645.2kB,使用 Gzip 來壓縮加上最小化後得到 294.2kB,使用 Brotli 壓縮更能達到 72.8kB

未壓縮最小化GzipBrotli
3645.2kB2936.0kB294.2kB72.8kB

雖然開發者模式看起來很肥大,但這就是原先的設計概念

為了提升更有效率的開發體驗,Tailwind 產出了上千個功能 class,其中可能有一大部分你根本不會使用到。

你可以把 Tailwind 想像成一大箱樂高,將其傾倒至地上後取出需要的部分來組合,完成後再將沒有用到的積木通通放回去箱子裡。

舉例來說,Tailwind 產出所有的 margin 功能,其包含全部尺寸的間距大小,以及於元素上你可能想使用的每一邊,再加上每一個你使用在專案中的斷點,這導致了上百種不同的組合,而這些功能全都是重要且必須可使用的,但不一定全部都是必須的。

當部屬生產模式時,你應該永遠使用 Tailwind 的 purge 選項,用來將未使用的樣式 tree-shake 並且優化最終編譯出來的檔案。 當 Tailwind 移除未使用的樣式後,最終壓縮後的 CSS 檔案幾乎很難超過 10kb。

撰寫可清除的 HTML

使用 purge 功能前,必須要優先了解它的運作方式以及建構正確的心智模型 (mental model),確保你永遠不會在部屬生產模式時意外地移除重要的樣式。

PurgeCSS (其實就是我們使用的函式庫) 刻意以單純的方式在 HTML 查找 class,它不會解析你的 HTML 來尋找 class 屬性或動態執行你的 JavaScript,它只是單純地在整個檔案尋找符合下方正規表達式的字串:

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

這基本上符合所有被空格、引號或角括號 (angle brackets) 區隔的字串,包含了 HTML 標籤、屬性、class,甚至是你實際書寫的內容。

Woman paying for a purchase
行銷
為您的新生意找到消費者

展開新生意是一項艱鉅的工作,五個點子讓您找到第一位消費者。

<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">
      行銷
    </div>
    <a href="/get-started" class="block mt-1 text-lg leading-tight font-semibold text-gray-900 hover:underline">
      為您的新生意找到消費者
    </a>
    <p class="mt-2 text-gray-600">
      展開新生意是一項艱鉅的工作,五個點子讓您找到第一位消費者。
    </p>
  </div>
</div>

這代表你必須完全地避免在你的樣板中動態新增 class 來串接字串,否則 PurgeCSS 將無法了解並保留那些 class。

避免在你的樣板中使用字串串接來新增 class

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

動態選擇完整的 class 名稱

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

只要 class 名稱完整出現在你的樣板之中,PurgeCSS 將不會移除它。

移除未使用的 CSS

基本用法

首先,在 purge 選項下以陣列規範所有樣板檔案的路徑:

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

這個清單應該包含 任何 在專案裡以名稱引用任何你的樣式的檔案。舉例來說,如果你的專案裡面有一個在 HTML 檔上面動態切換 class 的 JS 檔,你應該要確定該檔案被納入清單。

When using important with the selector strategy, be sure that the template file that includes your root selector is included in your purge configuration, otherwise all of your CSS will be removed when building for production.

現在只要在 NODE_ENV 設定為 production 的情況下編譯 CSS,Tailwind 將會自動從你的 CSS 中清除未使用的樣式。

手動啟動

如果想要手動控制是否要刪除未使用的樣式 (而不是依賴隱含的環境變數),你可以使用物件語法提供一個 enabled 選項,再以 content 選項來指定你的樣板。

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

我們只建議在生產模式下才移除未使用之樣式,在開發模式下移除它們代表每一次改動樣板時就需要重新編譯一次,再加上啟用 PurgeCSS 的情況下導致速度更慢。

Safelisting specific classes

If you need to safelist specific classes to make sure they are never accidentally removed from your CSS (perhaps because they are used in content that comes from a database or similar), you can use our top-level safelist option:

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

Transforming content

Sometimes you are authoring content in a format that compiles to HTML, and it would be better to compile that content to HTML before looking for potential classes. A great example of this is working with markdown files.

You can use the transform option to tell Tailwind to transform any file matching a specific extension before it looks for classes to guarantee the most accurate results:

// tailwind.config.js
let remark = require('remark')

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

Customizing extraction logic

If you need to override the logic Tailwind uses to detect classes in the content of a specific file type, you can use the extract option to provide a function that will be used to detect potential classes in matching files:

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

This is an advanced feature and most users won’t need it. The default extraction logic in Tailwind works extremely well for almost all projects.

保留 HTML 元素

預設情況下,Tailwind 會保留所有用於 HTML 基本元素上的 CSS 樣式,例如 htmlbodyph1 …等等標籤上的樣式,這是為了將過於清除的情形最小化的作法,比如說,使用 markdown 檔案 (此種檔案並不實際存在 h1 標籤),或是使用框架時可能會將 document shell (包含 htmlbody 標籤) 隱藏在某處的 vendor 目錄 (例如 Next.js)。

如果你想要停用這個行為,可以將 preserveHtmlElements 設為 false:

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

我們並不建議這樣設定,通常是壞處多於好處,但我們不是警察,你想怎樣就怎樣。

移除特定的 layers

By default, Tailwind will purge all styles in the base, components, and utilities layers. If you’d like to change this, use the layers option to manually specify the layers you’d like to purge:

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

移除全部未使用之樣式

預設情形下,Tailwind 僅會移除其自行生成樣式中未使用的部分,或是已經被明確包在 @layer 的指令 (directive) 裡面,並 不會 移除你自行額外加入專案裡的第三方插件中未使用的 CSS 樣式,例如 datepicker。

/* 這些樣式會被清除 */
@tailwind base;
@tailwind components;
@tailwind utilities;

/* 這些樣式會被清除 */
@layer components {
  .btn { /* ... */ }
}

/* 這些樣式不會被清除 */
.flatpickr-innerContainer { /* ... */ }
.flatpickr-weekdayContainer { /* ... */ }
.flatpickr-weekday { /* ... */ }

這是為了避免意外地移除你可能需要但沒有直接出現在樣板中的樣式,例如你從 node_modules 資料夾深處之中引用一部分依賴檔案 (dependencies) 的 class。

如果你真的想要移除 全部 未使用樣式,請設定 mode: 'all'preserveHtmlElements: false 加上非常小心地寫入 全部 可能引用任何 class 或 HTML 元素的檔案路徑。

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

我們並不建議此作法,你會發現使用保守的預設設定,將會取得超過 99% 的檔案大小優勢。

移除未使用的 keyframes

即便你不曾使用過 @keyframes,PurgeCSS 預設並不會將其移除,仍然可以在 stylesheet 裡找到被遺留下來與 animation 相關的樣式。你可以用 PurgeCSS options 底下的 keyframes 將前述樣式移除:

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

PurgeCSS 的 options 選項

如果你需要直接傳遞其他的設定給 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 的足跡。

預設的 theme 提供非常大量的 colors、breakpoints、sizes、margins …等等,來確保你使用 Tailwind 製作 prototype、CodePen 範例或只是嘗試工作流程時,可以保有愉快且流暢的體驗。

我們不希望你撰寫新的 CSS,只因為我們沒有提供足夠的 padding 功能,或是你想要一個橘色系的色彩計畫但我們卻只提供藍色給你。

不過你可能需要權衡一下:預設架構下的檔案會比有客製化設定檔的架構來的肥大許多。

接下來提供一些策略讓你可以建立小且高效能的 CSS 檔案。

限制你的調色盤

預設主題提供高達 84 色 的 backgrounds、borders、text 和 placeholders,而這些各自都包含了 hover:focus: 的變化模式 (variants),甚至還有預設的六個螢幕尺寸響應式變化模式。

預設情況下將會產生 上千個 與這些顏色有關的 class,它們甚至占了最終檔案大約一半的大小。

其實僅有極少的專案會用到這麼多的顏色,移除你不需要的顏色可以為整體檔案大小帶來極大的影響。

下面列舉了使用較少調色盤的最終檔案大小結果:

顏色原始大小MinifiedGzipBrotli
84 (預設)3645.2kB2936.0kB294.2kB72.8kB
502805.8kB2231.3kB238.7kB59.3kB
252177.6kB1702.8kB198.3kB50.6kB

移除未使用的斷點

由於幾乎每個 Tailwind 功能都是從每個螢幕尺寸複製而來的,使用更少的螢幕尺寸也可以為整體檔案大小帶來巨大的影響。

下面列舉了使用較少螢幕尺寸的最終檔案大小結果:

斷點原始檔案MinifiedGzipBrotli
5 (預設)3645.2kB2936.0kB294.2kB72.8kB
32395.9kB1936.0kB196.2kB62.3kB
21781.4kB1446.0kB147.4kB57.3kB
11167.3kB956.3kB98.6kB52.5kB

如果你只會用到 3 個螢幕尺寸與 35 色,你可以在不更改其他內容的情況下將其壓縮至 46.8kB

停用未使用的核心插件和變化模式

如果你預期在專案中不會使用到某些功能插件,可以在設定檔中把 corePlugins 設為 false 來完全停用它們:

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

如果你只會用到少量的功能,可以在 corePlugins 設定欲保留的功能插件陣列。

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

上面的範例將會停用除了 margin 和 padding 之外的功能。

如果你不需要響應式功能,將其變化模式設為空陣列可以減少產生 83% 的 class。

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

比起限制你的調色盤或使用較少的斷點,這些只是小小的勝利,但堆疊起來的檔案大小仍然不可小覷。