跳至內容

Svelte 作用域樣式

將每個 Svelte 組件的工具樣式產生的 CSS 直接放入 Svelte 組件的 <style> 區塊中,而不是放在全域 CSS 檔案中。

此組件

svelte
<div class="mb-1" />

將會轉換成

svelte
<div class="uno-ei382o" />

<style>
  :global(.uno-ei382o) {
    margin-bottom: 0.25rem;
  }
</style>

何時使用

使用案例說明使用的套件
小型應用程式擁有一個全域 CSS 檔案更方便。請使用一般的 Vite 外掛程式來處理 Svelte/SvelteKitunocss/vite
大型應用程式Svelte 作用域樣式可以幫助您避免不斷增長的全域 CSS 檔案。@unocss/svelte-scoped/vite
組件庫產生的樣式會直接放置在建置的組件中,而無需在使用應用程式的建置流程中使用 UnoCSS。@unocss/svelte-scoped/preprocess

運作方式

一般的 UnoCSS/Tailwind CSS 設定會將工具樣式放置在具有正確順序的全域 CSS 檔案中。相反,Svelte 作用域樣式會將您的樣式分佈到許多任意排序的 Svelte 組件 CSS 檔案中。然而,它必須保持工具樣式的全域性,以便它們可以根據上下文感知的需要,例如從右到左和其他 下方列出的使用案例。這帶來了一個挑戰,解決方法是使用 Svelte 的 :global() 包裝器來選擇退出預設的 Svelte CSS 雜湊方法,而是使用基於檔名 + 類別名稱的雜湊來編譯唯一的類別名稱,這些類別名稱可以在沒有樣式衝突的情況下設為全域。

用法

由於 Svelte 作用域樣式會重寫您的工具類別名稱,因此您可以編寫它們的位置會受到限制

支援的語法範例
類別屬性<div class="mb-1" />
類別指令<div class:mb-1={condition} />
類別指令簡寫<div class:logo />
類別屬性<Button class="mb-1" />

Svelte 作用域樣式旨在作為使用工具樣式的專案的直接替代品。因此,也支援在類別屬性中找到的表達式(例如 <div class="mb-1 {foo ? 'mr-1' : 'mr-2'}" />),但我們建議您今後使用類別指令語法。另請注意,如果您以其他方式使用類別名稱,例如將它們放在 <script> 區塊中或使用屬性化模式,那麼在使用 Svelte 作用域樣式之前,您需要採取額外的步驟。您可以利用 safelist 選項,也可以查看下方的 預設集 部分以獲取更多提示。

上下文感知

即使樣式分佈在應用程式的 Svelte 組件中,它們仍然是全域類別,並且會與特定組件外部的元素相關聯。以下是一些範例

依賴父組件

依賴父組件中屬性的類別

svelte
<div class="dark:mb-2 rtl:right-0"></div>

將會轉換成

svelte
<div class="uno-3hashz"></div>

<style>
  :global(.dark .uno-3hashz) {
    margin-bottom: 0.5rem;
  }
  :global([dir="rtl"] .uno-3hashz) {
    right: 0rem;
  }
</style>

子組件影響

您可以在 3 個子元素之間添加空格,其中一些子元素位於不同的組件中

svelte
<div class="space-x-1">
  <div>Status: online</div>
  <Button>FAQ</Button>
  <Button>Login</Button>
</div>

將會轉換成

svelte
<div class="uno-7haszz">
  <div>Status: online</div>
  <Button>FAQ</Button>
  <Button>Login</Button>
</div>

<style>
  :global(.uno-7haszz > :not([hidden]) ~ :not([hidden])) {
    --un-space-x-reverse: 0;
    margin-left: calc(0.25rem * calc(1 - var(--un-space-x-reverse)));
    margin-right: calc(0.25rem * var(--un-space-x-reverse));
  }
</style>

將類別傳遞給子組件

您可以在組件中添加 class 屬性,以便在使用該組件的任何地方傳遞自定義類別。

svelte
<Button class="px-2 py-1">Login</Button>

將會轉換成

svelte
<Button class="uno-4hshza">Login</Button>

<style>
  :global(.uno-4hshza) {
    padding-left:0.5rem;
    padding-right:0.5rem;
    padding-top:0.25rem;
    padding-bottom:0.25rem;
  }
</style>

在接收組件中實作類別的一個簡單方法是使用 {$$props.class} 將它們放置在元素上,例如 div class="{$$props.class} foo bar" />

套用指令

您可以在 <style> 區塊中使用套用指令,使用 --at-apply@apply 或使用 applyVariables 選項設定的自定義值。

Svelte 作用域樣式甚至可以正確處理上下文相關的類別,例如 dark:text-white,而一般的 @unocss/transformer-directives 套件無法正確處理,因為它不是專為 Svelte 樣式區塊建置的。例如,使用 Svelte 作用域樣式,此組件

svelte
<div />

<style>
  div {
    --at-apply: rtl:ml-2;
  }
</style>

將會轉換成

svelte
<div />

<style>
  :global([dir=\\"rtl\\"]) div {
    margin-right: 0.5rem;
  }
</style>

為了使 rtl:ml-2 正常運作,[dir="rtl"] 選擇器會使用 :global() 包裝,以防止 Svelte 編譯器自動將其刪除,因為組件中沒有具有該屬性的元素。然而,div 不能包含在 :global() 包裝器中,因為該樣式會影響應用程式中的每個 div

其他樣式區塊指令

支援使用 theme(),但不支援 @screen

Vite 外掛程式

在 Svelte 或 SvelteKit 應用程式中,將產生的樣式直接注入到您的 Svelte 組件中,同時將最必要的樣式放置在全域樣式表中。在 Stackblitz 中查看 SvelteKit 範例

Open in StackBlitz

安裝

bash
pnpm add -D unocss @unocss/svelte-scoped
bash
yarn add -D unocss @unocss/svelte-scoped
bash
npm install -D unocss @unocss/svelte-scoped

新增外掛程式

@unocss/svelte-scoped/vite 新增到您的 Vite 設定中

vite.config.ts
ts
import { sveltekit } from '@sveltejs/kit/vite'
import UnoCSS from '@unocss/svelte-scoped/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    UnoCSS({
      // injectReset: '@unocss/reset/normalize.css', // see type definition for all included reset options or how to pass in your own
      // ...other Svelte Scoped options
    }),
    sveltekit(),
  ],
})

新增設定檔

如下 所述 設定您的 uno.config.ts 檔案。

全域樣式

雖然幾乎所有樣式都放置在個別組件中,但仍然有一些樣式必須放置在全域樣式表中:預設樣式、安全列表和可選的重置(如果您使用 injectReset 選項)。

%unocss-svelte-scoped.global% 預留位置新增到您的 <head> 標籤中。在 Svelte 中,這是 index.html。在 SvelteKit 中,這將在 app.html 中的 %sveltekit.head% 之前

index.html
html
<head>
  <!-- ... -->
  <title>SvelteKit using UnoCSS Svelte Scoped</title>
  %unocss-svelte-scoped.global%
  %sveltekit.head%
</head>

如果使用 SvelteKit,您還必須將以下內容新增到 src/hooks.server.js 檔案中的 transformPageChunk 鉤子中

src/hooks.server.js
js
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
  const response = await resolve(event, {
    transformPageChunk: ({ html }) =>
      html.replace(
        '%unocss-svelte-scoped.global%',
        'unocss_svelte_scoped_global_styles'
      ),
  })
  return response
}

此轉換必須位於 路徑包含 `hooks` 和 `server` 的檔案中(例如 `src/hooks.server.js`、`src/hooks.server.ts`),因為 `svelte-scoped` 將會在您的伺服器鉤子檔案中尋找,以使用您的全域樣式替換 `unocss_svelte_scoped_global_styles`。請確保不要從其他檔案匯入此轉換,例如當使用 sequence 從 `@sveltejs/kit/hooks` 匯入時。

在一般的 Svelte 專案中,Vite 的 transformIndexHtml 鉤子會自動執行此操作。

Svelte 預處理器

使用工具樣式來建置一個不依賴於包含配套 CSS 檔案的組件庫,方法是使用預處理器將產生的樣式直接放置到建置的組件中。在 Stackblitz 中查看 SvelteKit 程式庫範例

Open in StackBlitz

安裝

bash
pnpm add -D unocss @unocss/svelte-scoped
bash
yarn add -D unocss @unocss/svelte-scoped
bash
npm install -D unocss @unocss/svelte-scoped

新增預處理器

@unocss/svelte-scoped/preprocess 新增到您的 Svelte 設定中

svelte.config.js
ts
import adapter from '@sveltejs/adapter-auto'
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
import UnoCSS from '@unocss/svelte-scoped/preprocess'

const config = {
  preprocess: [
    vitePreprocess(),
    UnoCSS({
      // ... preprocessor options
    }),
  ],
  // other Svelte config
}

不要在開發過程中合併類別名稱

在一般應用程式中使用 Svelte 作用域樣式時,Vite 外掛程式會自動偵測 `dev` 與 `build`。在開發過程中,類別將會保持 distinct 並且就地雜湊,以便在瀏覽器的開發者工具中輕鬆地開啟/關閉。`class="mb-1 mr-1"` 將會變成類似 `class="_mb-1_9hwi32 _mr-1_84jfy4"` 的東西。在生產環境中,這些類別將會使用您想要的前綴(預設為 `uno-`)以及基於檔名 + 類別名稱的雜湊編譯成單個類別名稱,例如 `class="uno-84dke3"`。

如果您希望在使用預處理器時也具有相同的行為,則必須根據環境手動設定 `combine` 選項。一種方法是安裝 cross-env 並將您的開發指令碼更新為

"dev": "cross-env NODE_ENV=development vite dev"

然後調整您的 svelte.config.js

diff
+const prod = process.env.NODE_ENV !== 'development'
const config = {
  preprocess: [
    vitePreprocess(),
    UnoCSS({
+      combine: prod,
    }),
  ],
}

新增設定檔

如下 所述 設定您的 uno.config.ts 檔案。

預設樣式

使用預處理器時,您可以選擇將預設樣式包含在需要的特定組件中,方法是將 `uno-preflights` 作為樣式屬性新增。

html
<style uno-preflights></style>

任何以句點開頭的特殊預設樣式,例如 ` .prose :where(a):not(:where(.not-prose, .not-prose *))`,都將使用 `:global()` 包裝,以避免被 Svelte 編譯器自動刪除。

如果您的類別不依賴預設樣式,或者您建置的組件僅在已包含預設樣式的應用程式中使用,則無需將預設樣式新增到個別組件中。

安全列表

使用預處理器時,您可以選擇將安全列表類別包含在組件中,方法是將 `uno-safelist` 作為樣式屬性新增。

html
<style uno-safelist></style>

您的安全名單樣式將會被 :global() 包裹,以避免被 Svelte 編譯器自動移除。

設定

將您的 UnoCSS 設定放置在 uno.config.ts 檔案中

uno.config.ts
ts
import { defineConfig } from 'unocss'

export default defineConfig({
  // ...UnoCSS options
})

由於一般 UnoCSS 全域使用方式和 Svelte 作用域使用方式的差異,因此不支援提取器。預設集和轉換器則如同以下章節所述般受到支援。其他詳細資訊請參閱設定檔設定參考

預設集支援

由於需要在全域樣式表中設定一些必要的樣式,而其他所有樣式則依需求包含在每個元件中,因此需要根據具體情況處理預設集。

預設集是否支援備註
@unocss/preset-uno@unocss/preset-mini@unocss/preset-wind@unocss/preset-icons@unocss/web-fonts這些以及所有僅依賴規則/變體/預設樣式的社群插件,例如 unocss-preset-forms,皆可正常運作。
@unocss/preset-typography由於此預設集會將規則集新增到您的預設樣式中,因此在使用此預設集時,您必須將 prose 類別新增到您的安全名單中,否則預設樣式將永遠不會被觸發。此預設集中的所有其他類別,例如 prose-pink,可以設定為元件作用域。
@unocss/preset-rem-to-px此預設集以及所有僅修改樣式輸出的類似預設集皆可正常運作。
@unocss/preset-attributify-此預設集無法運作。請改用 unplugin-attributify-to-class Vite 插件 (attributifyToClass({ include: [/\.svelte$/]})),並將其放置在 Svelte Scoped Vite 插件之前。
@unocss/preset-tagify-新增自定義提取器的預設集將無法運作。請建立一個預處理器,將 <text-red>Hi</text-red> 轉換為 <span class="text-red">Hi</span>,然後建立一個 PR 將連結新增到這裡。

對於其他預設集,如果它們不依賴傳統的 class="..." 用法,您需要先將這些類別名稱預處理成 class="..." 屬性。如果它們新增了類似 typography 的 .prose 類別的預設集,則您需要將觸發預設集新增的類別放置到您的安全名單中。

轉換器支援

您的 CSS 檔案 (css|postcss|sass|scss|less|stylus|styl) 支援轉換器。要使用它們,請將轉換器新增到 vite.config.ts 中的 cssFileTransformers 選項。

vite.config.ts
ts
import transformerDirectives from '@unocss/transformer-directives'

export default defineConfig({
  plugins: [
    UnoCSS({
      cssFileTransformers: [transformerDirectives()],
    }),
    sveltekit(),
  ],
})

資訊

由於 Svelte Scoped 的運作方式,Svelte 元件中不支援轉換器。

作用域的工具類別釋放創造力

關於何時可能想要使用作用域樣式的一些建議:如果您在大型專案的開發過程中,每次使用像 .md:max-w-[50vw] 這樣的類別時,您知道它只會使用一次,但您卻會因為全域樣式表的大小越來越大而感到畏懼,那麼請嘗試使用這個套件。猶豫是否要使用您需要的類別會抑制創造力。當然,您可以在樣式區塊中使用 --at-apply: md:max-w-[50vw],但這很繁瑣,而且上下文中的樣式很有用。此外,如果您想在您的專案中包含各種圖示,您將會開始感受到將它們新增到全域樣式表的負擔。當每個元件都承擔自身樣式和圖示的重量時,您可以繼續擴展您的專案,而無需分析每個新增項目的成本效益。

授權條款

以 MIT 授權條款釋出。