網站導覽

基本概念

渲染模式

選擇 CSS 的生成位置,比較靜態、預先渲染、執行階段與漸進式傳遞,並驗證結果。

Choose when CSS is generated and how it reaches the page.

本頁目錄

#概覽

依應用程式何時得知完整類別名稱,選擇渲染模式。名稱能在原始碼中取得時,先使用靜態渲染。預先渲染會從完成的頁面 HTML 生成 CSS;執行階段渲染處理瀏覽器中建立的新類別;漸進式渲染則結合頁面 CSS 與執行階段支援。

請先完成框架的安裝指南。官方整合會載入 CSS 進入點並設定所選模式;下方範例說明各模式的行為與驗證方式。框架支援及可供處理的 HTML,取決於使用的整合。

#選擇渲染模式

模式類別來源初始 CSS瀏覽器中的 Master CSS
靜態原始檔中的完整字串生成的樣式表不需要執行階段或引擎
預先渲染渲染完成的頁面 HTML隨該頁面傳遞的規則不需要執行階段或引擎
執行階段已連接的 DOM 元素執行階段啟動後生成規則執行階段與引擎
漸進式渲染完成的 HTML,接著是後續 DOM 變更隨該頁面傳遞的規則執行階段追蹤使用情況;引擎可延後到出現未涵蓋類別時才啟動

互動式應用程式也能使用靜態 CSS。切換已知類別、修改 CSS 變數及使用原生選擇器,都不需要執行階段引擎。完整類別名稱在建置後才首次出現時,才需要考慮執行階段生成。

預先渲染需要真實的頁面標記。用戶端渲染應用中的空根元素,無法提供應用程式類別供伺服器渲染器處理。也請檢查整合的適用邊界:Next.js 漸進式轉接器處理建置時 HTML,因此請求時的動態 SSR 回應依賴瀏覽器執行階段生成規則。


#靜態渲染

建置會掃描原始碼中的完整類別字串,再依專案定義生成 CSS,不會執行應用程式碼或觀察 DOM。瀏覽器載入輸出的樣式表,不需要 Master CSS 執行階段。

#生成樣式表的進入點

在應用程式原本就會載入的樣式表中,匯入 Master CSS:

src/style.css
@import '@master/css';

這個匯入是生成 utility、其必要資源與預設集基礎樣式的插入位置。每個生成樣式表根進入點使用一個匯入。請保留在最上層匯入區塊中、一般樣式規則之前;該區塊的其他 CSS 匯入會排在生成的 Master CSS 之前輸出。

若要觀察從原始碼到 CSS 的流程,請在匯入之後加入這個 utility 定義,並將按鈕放在會被掃描的原始檔中:

@utility btn {  display: inline-flex;  padding-inline: var(--spacing-md);}
<button class="btn text-center">Save</button>
產生的 CSS
@layer theme {  :root,  :host {    --spacing-md: 1rem  }}@layer utilities {  .btn {    display: inline-flex;    padding-inline: var(--spacing-md)  }  .text-center {    text-align: center  }}

掃描器會找到 btn 與 text-center。引擎解析專案組合樣式與預設集的固定 utility,再生成規則與參照的資源。只定義 btn 不會輸出規則,必須由掃描到的類別或明確的安全清單提出需求。

整合會探索一般應用程式來源,並在開發時更新 CSS。原始碼應保留完整類別名稱,變動值則透過 CSS 變數傳遞;掃描類別名稱說明這兩種方式,以及 @source 與安全清單的例外處理。

#原生 CSS 進入點

CSS 進入點中的原生選擇器預設保留,即使掃描來源中沒有其類別也一樣。它們的 var(--*) 與動畫參照,可保留相符的主題變數與關鍵影格。

確定要修剪專案自有的原生 CSS 時,使用 pruneNativeCSS: true;只針對單一檔案則使用 @prune native;。@preserve native; 可將檔案排除於整合層級的修剪之外。請參閱修剪未使用的原生 CSS。


稍後出現的類別無法擴充靜態樣式表

此範例假設所有建置輸入與安全清單都沒有 p-lg。動態值可透過自訂屬性改變,不必更換類別。

文字說明

靜態建置看見 p-md 並輸出規則。瀏覽器稍後加入 p-lg,但該輸出中沒有它的規則。

#執行階段渲染

類別名稱在瀏覽器才得知時,例如視覺編輯器或建置後載入的內容,請使用執行階段渲染。執行階段透過 MutationObserver 追蹤已連接元素及其類別變更,再插入或重用相符 CSS 規則。

執行階段傳遞

瀏覽器引擎插入或重用 CSS 規則。快取規則的存續時間可能超過已連接 DOM 的使用期間。

文字說明
  1. 觀察. 讀取已連接元素,並接收 DOM 類別變更。
  2. 確保規則可用. 透過編譯後的專案程式執行完整類別。
  3. 更新. 在 runtime 樣式表中插入或重用原生 CSS 規則。

透過整合選擇執行階段模式,例如 Vite 執行階段設定。啟動後,在瀏覽器主控台逐一執行下列片段:

  1. 插入元素

    const h1 = document.createElement('h1')h1.className = 'text-center'h1.textContent = 'Hello World'document.body.append(h1)

    執行階段生成規則後,已連接的標題就會置中。

  2. 加入類別

    h1.classList.add('(font-size:3rem)')

    標題增大至 3rem,原有對齊規則仍持續使用。

  3. 移除類別

    h1.classList.remove('text-center')

    標題不再符合對齊規則,會恢復為繼承的對齊方式,即使執行階段暫時仍保留未使用的規則也一樣。

  4. 移除元素

    h1.remove()

    這個標題不再使用兩個類別。若沒有其他已連接元素使用它們,規則就可在閒置清理期間移除。

兩個類別同時存在時,使用下列規則:

產生的 CSS
@layer utilities {  .text-center {    text-align: center  }  .\(font-size\:3rem\) {    font-size: 3rem  }}

執行階段生成依賴 JavaScript 啟動。若頁面初始規則必須隨回應提供,請使用預先渲染 CSS;只有後續類別名稱需要時,才加入執行階段支援。

#執行階段啟動前隱藏頁面

僅使用執行階段的頁面在生成前沒有規則。可在根元素加入 hidden,避免未套樣式的畫面先被繪製:

<html lang="en" hidden>  <!-- Load the stylesheet and runtime using your integration's setup. --></html>

執行階段啟動後會移除 hidden,它所處理的啟動錯誤流程也會取消根元素隱藏。但如果 JavaScript 停用,或執行階段指令碼遭封鎖、下載失敗,頁面會一直隱藏。這些情況仍必須可讀的內容,應使用靜態或預先渲染 CSS。漸進式頁面已帶有首屏 CSS,因此維持可見。

#預先載入執行階段程式

引擎必須取得程式後,才能生成第一批規則。僅使用執行階段的頁面應預先載入程式,讓請求在解析文件時就開始。使用 as="fetch" 與 crossorigin 配合執行階段的請求,同源時也一樣:

<!DOCTYPE html><html lang="en" hidden><head>  <link rel="preload" as="fetch" crossorigin href="/master-css/default-program.bin">   <link rel="stylesheet" href="/master-css/base.css">  <script src="/master-css/global.min.js"></script></head></html>
檔案是否預先載入?
CDN default-program.bin是,適用於使用封裝預設集、僅採 CDN 執行階段的頁面。
專案程式 master-css-program.<hash>.bin僅使用執行階段的頁面需要,官方執行階段整合會加入提示。漸進式頁面不需要,引擎與程式會等到某個類別需要時才載入。
頁面 hydration manifest不需要。這是交接資料,瀏覽器不靠它繪製畫面。

在開發者工具確認執行階段重用預先載入的回應,沒有重新請求。配對規則請參閱 rel="preload"。

預載入取得程式,執行階段啟動時才使用

圖示呈現僅使用執行階段的頁面,以及相符的 fetch 預載入。連線長度不代表請求時長或啟動速度。

文字說明
  1. 含有 as="fetch" 與相符 crossorigin 的預載入,會在文件解析期間開始請求程式。
  2. 這個提示不會執行引擎、檢查 DOM class 或生成 CSS。執行階段初始化才會使用相符回應並執行這些工作。
  3. 刻意延後引擎與程式載入的漸進式頁面,不應加入這個程式預載入。頁面的 hydration manifest 是另一項資源。

#預先渲染 — SSG、SSR

建置或伺服器取得最終頁面 HTML,且後續類別不需要瀏覽器生成時,請使用預先渲染。伺服器渲染器讀取 HTML、生成規則,並插入 style#master-css。它從標記生成 CSS,不會將應用程式元件渲染成 HTML。

官方整合會對支援的 HTML 輸出執行此步驟。自訂伺服器或建置流程可參考此範例,使用建置整合提供的 virtual:master-css-manifest。伺服器程式碼執行前,建置工具必須先解析這個虛擬模組。

index.html
<!doctype html><html lang="en"><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width, initial-scale=1"></head><body>  <h1 class="font-5xl">Hello World</h1></body></html>
server.ts
import { readFileSync } from 'node:fs'import { createServerRenderer } from '@master/css-server'import manifest from 'virtual:master-css-manifest'const indexHTML = readFileSync('./index.html', 'utf-8')const renderer = createServerRenderer({ manifest, maxCachedClasses: Infinity })const { html } = renderer.renderHTML(indexHTML)renderer.dispose()

寫入或回傳取得的 html。其中的 style#master-css 包含標題規則與所需 font-size token:

產生的 CSS
@layer theme {  :root,  :host {    --font-size-5xl: 2.5rem  }}@layer utilities {  .font-5xl {    font-size: var(--font-size-5xl)  }}

此輸出不需要 Master CSS 執行階段或 hydration manifest。只在稍後出現的類別,需要另外提供 CSS,或選擇支援執行階段的模式。

在有限次數的靜態建置中,渲染器可重用快取規則,同時產生每頁專屬 CSS。manifest 改變時重新建立渲染器,建置結束後則釋放。長期運作的伺服器應設定有上限的 maxCachedClasses,不要沿用建置範例的 Infinity;請參閱伺服器 API。


#漸進式渲染

伺服器或建置可生成頁面初始 CSS,且瀏覽器稍後可能引入新類別時,請使用漸進式渲染。回應透過 style#master-css 帶有 CSS;執行階段讀取頁面 hydration manifest,區分已涵蓋類別與需要引擎的類別。

漸進式傳遞

初始 CSS 隨 HTML 抵達。預先渲染樣式表與頁面 manifest 有效時,引擎會持續延後啟動,直到出現 manifest 之外的已連接類別。

文字說明
  1. 渲染. 從渲染後頁面 HTML 的類別生成 CSS。
  2. 傳遞. 將初始 CSS 隨 HTML 送出,讓瀏覽器在 runtime 啟動前就能套用。
  3. 接續運作. 讀取頁面 hydration manifest 並追蹤已連接 DOM 類別。預設情況下,manifest 之外的類別會載入瀏覽器引擎與專案程式、採用相符既有規則,再生成額外 CSS。

一般輸出會讓 style 元素指向外部 manifest:

<style id="master-css" data-master-css-hydration-manifest="/_master-css/hydration/master-css-hydration.<hash>.json">...</style>

實際資產 URL 由整合提供。無法發布穩定 JSON 資產的自訂 SSR 主機,也可改用行內 script#master-css-hydration-manifest。頁面若沒有生成規則或受管理資源,就不需要 manifest 指標。

瀏覽器可在執行階段或 manifest 載入前套用行內 CSS。這說明 CSS 何時可用;實際繪製時機仍取決於頁面其他資源。不要預先載入頁面 hydration manifest 或延後載入的專案程式;請參閱預先載入。

manifest 中列出的類別可直接重用,不必啟動引擎。這包含一般撰寫的類別,以及由生成或專案選擇器參照、不含自身規則的狀態或主題類別。變更已渲染選擇器所使用的屬性,也不需要新的類別規則。完整類別若不在 manifest 中,即使曾出現在專案其他原始碼裡,仍會啟動引擎。

已知類別重用已傳遞的 CSS

這是預先渲染產物有效時的預設漸進式交接,不是效能時間軸。

文字說明

瀏覽器已有 CSS 與有效頁面 manifest。已知且已連接的類別沿用既有 CSS,manifest 之外的類別則觸發延後載入引擎的分支。

#CSS hydration

hydration 讓執行階段採用既有 CSS 規則。啟動時會讀取明確設定的 hydrationManifest 選項,或尋找行內 manifest、style#master-css 上的 URL。引擎延後啟動時,執行階段會追蹤使用情況,不重建樣式表。

CSS hydration

引擎啟動時採用相符規則,不符則重建 CSS。參照的 manifest 若無法載入或驗證,啟動會在此比較之前遭拒絕。

文字說明
  1. 讀取. 載入頁面 hydration manifest,並繼續使用既有樣式表。
  2. 驗證. 瀏覽器引擎啟動時,使用專案程式依伺服器規則順序重播 manifest 類別、驗證資源,並比較生成規則與既有 CSSOM。
  3. 採用或重建. 將相符規則重用為 runtime 管理的規則物件;不符時則重建 CSS。

新類別或 cssRuntime.ready() 啟動引擎時,瀏覽器引擎會依伺服器規則順序重播 manifest 中的類別、驗證資源,再將生成結果與既有 CSSOM 比較。相符規則成為執行階段管理的規則,新類別隨後可生成額外 CSS。移除的類別可能在交接前仍保留預先渲染規則,交接後才進入一般保留規則的清理流程。

manifest 帶有資源順序,以及頁面樣式表已輸出的全域資源,不會重複 CSS 文字,也不取代專案程式。請讓它與同一次建置的 HTML、資產一起提供。


#整合支援

這些官方轉接器預設使用靜態渲染。建置階段尚未知的類別,需明確選擇執行階段生成。pre-render 從 HTML 生成 CSS,不需要用戶端執行階段;progressive 則結合 HTML 渲染與支援後續類別的執行階段。各轉接器能處理的 HTML 取決於主框架:

轉接器靜態執行階段pre-render 與伺服器 HTMLprogressive
Vite建置階段擷取透過 Vite HTML 流程注入Vite 掛鉤處理的 HTML;自訂 SSR 需要伺服器整合Vite HTML 渲染加上執行階段;自訂 SSR 需自行渲染 HTML
Webpack建置階段擷取注入編譯的 HTML 資產HTML 主機必須呼叫 @master/css-server;外掛沒有請求時伺服器掛鉤提供執行階段傳遞;主機提供渲染後 HTML 與 hydration 資料
Next.js建置階段擷取用戶端 instrumentation建置後處理 HTML 輸出;不轉換請求時動態 SSR建置渲染 HTML 加上執行階段;動態 SSR 不會自動注入伺服器 CSS
Astro透過 Vite 在建置階段擷取注入頁面指令碼Astro 中介軟體處理預先渲染頁面與伺服器 HTML 回應中介軟體渲染加上執行階段
Nuxt透過 Vite 在建置階段擷取Nuxt 用戶端外掛內容主體為字串的 Nitro HTML 回應;不轉換串流主體Nitro HTML 渲染加上用戶端執行階段
SvelteKit透過 Vite 在建置階段擷取需要文件所述的用戶端掛鉤需要 Master CSS 伺服器 handle;在 SSR 與預先渲染期間處理頁面區塊同時需要伺服器 handle 與用戶端掛鉤

框架採用 Vite 或 Webpack,不代表該 bundler 能為它加入伺服器行為。Webpack 的 HTML 注入也不會重寫由開發伺服器靜態目錄直接提供的檔案。Astro 中介軟體在回傳前讀取完整 HTML 回應,SvelteKit 則使用頁面區塊掛鉤。選擇傳遞方式時,請依框架指南操作。PHP、Rails、CMS 與 .NET 主機會透過 Vite 建置資產,再從樣板載入;請參閱伺服器渲染應用程式。

#原生修剪是另一項選擇

漸進式渲染可搭配原生修剪,不改變初始 CSS 傳遞或後續 utility 生成的時機。修剪需要已知使用集合,以及會提供該集合的建置流程;執行階段生成無法還原已從傳遞內容移除的任意原生選擇器。可重現的比較請參閱最佳化 CSS 傳遞。

#驗證所選模式

除了開發環境,也要測試正式建置:整合即使在正式環境預先渲染 CSS,開發時仍可能使用執行階段生成。

模式驗證項目
靜態生成 HTML 有載入樣式表,且具代表性的完整來源類別在其中有規則。停用 JavaScript 後,這些樣式仍保留。
預先渲染回應原始碼包含帶有頁面規則的 style#master-css。請在執行 JavaScript 前驗證,僅檢查即時 DOM 無法證明預先渲染已發生。
執行階段插入初始 DOM 沒有的類別,在執行階段觀察到後檢查計算樣式與生成的 CSS 規則。
漸進式先驗證預先渲染 CSS,再加入頁面 manifest 沒有的類別並檢查其規則是否生成。轉接器若只處理建置時 HTML,請另外測試動態 SSR 路由。

#檢查運作中的樣式表

執行階段擁有 style#master-css 並更新其 CSSOM。啟動後,元素文字內容無法作為可靠的即時檢視,請改查樣式表的 cssRules:

const sheet = document.querySelector('style#master-css').sheetconsole.log(sheet.cssRules)

#診斷漸進式啟動

觀察結果意義與下一項檢查
警告後重建 CSS既有規則與 hydration 資料不符,或 style 沒有 manifest 指標。請確認 HTML、CSS 與 manifest 資產來自同一次建置。
啟動以 INVALID_HYDRATION_MANIFEST 拒絕參照的行內或外部 manifest 無法載入或通過驗證。請檢查資產 URL、回應與部署檔案。
初始 CSS 正常,新類別卻無效延後啟動的引擎可能載入失敗。請檢查 onDiagnostic、程式請求與 cssRuntime.snapshot().engine。既有預先渲染 CSS 會保留;refresh() 或重新 start() 可重試。

新類別很可能在載入後不久出現時,enginePrefetch: 'idle' 會在閒置時啟動引擎。使用 engine: 'eager' 則在 start() 期間啟動。兩者都改變預設延後啟動的取捨,應依頁面實際互動選用。

#互動密集的畫面

一次互動若加入大量首次出現的類別,瀏覽器就必須同時處理 DOM 變更與新樣式表規則。對編輯器、資料網格、建構工具或其他大型動態檢視:

  • 將穩定的版面、排版與元件狀態保留在共用類別中。
  • 引擎就緒後、插入檢視之前,先預熱可預期的執行階段類別。
  • 座標、使用者產生的色彩等各元素經常變動的值,使用 CSS 變數或行內樣式。
  • 重複狀態若不需要為每個值生成不同類別,請使用原生選擇器。

若要預熱檢視中可預期的類別,請使用 MasterCSSRuntime.start() 回傳的 handle:

await cssRuntime.ready()cssRuntime.ensureClassRules(['grid', 'gap-md'])

請一起測量執行階段 JavaScript、程式傳遞、初始化、DOM 觀察與 CSSOM 處理。單看壓縮後 JavaScript 大小,無法說明互動成本。測量結果與條件請參閱基準測試。

#使用者產生的標記

執行階段觀察或伺服器渲染的每個類別都會成為 CSS,因此 class 屬性與 style 屬性一樣強大,包含原生宣告、url() 及 :has() 等選擇器。頁面渲染他人撰寫的標記,例如留言、CMS 豐富文字或聊天訊息時,HTML 淨化器應以處理 style 的方式處理 class:移除,或只允許自行挑選的類別。只跳脫屬性值並不足夠,因為類別名稱本身就是輸入。

執行階段狀態、傳遞產物、自訂啟動進入點與引擎主機,請參閱 執行階段、server 與 engine 套件。