Skip to content

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>

何時使用

使用場景描述使用的包
小型應用使用 1 個全局 CSS 文件更方便。使用 Svelte/SvelteKit 的常規 Vite 插件。unocss/vite
大型應用Svelte 作用域可以幫助你避免不斷增長的全局 CSS 文件。@unocss/svelte-scoped/vite
組件庫生成的樣式直接放在構建的組件中,無需在使用應用的構建管道中使用 UnoCSS。@unocss/svelte-scoped/preprocess

工作原理

常規的 UnoCSS/Tailwind CSS 設置會將實用樣式放在具有適當順序的全局 CSS 文件中。相比之下,Svelte 作用域將樣式分佈在許多任意排序的 Svelte 組件 CSS 文件中。但是,它必須保持實用樣式為全局樣式,以允許它們在需要時具有上下文感知能力,比如從右到左和下面列出的其他用例。這提出了一個挑戰,通過使用 Svelte 的 :global() 包裝器來解決,該包裝器選擇退出默認的 Svelte CSS 哈希方法,而是使用基於文件名 + 類名的哈希來編譯唯一的類名,這些類名可以在沒有樣式衝突的情況下全局化。

使用

由於 Svelte 作用域重寫了你的實用類名,你可以寫類名的位置是有限的:

支持的語法示例
Class 屬性<div class="mb-1" />
Class 指令<div class:mb-1={condition} />
Class 指令簡寫<div class:logo />
Class 屬性<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 示例

在 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

添加插件

在 Vite 配置中添加 @unocss/svelte-scoped/vite

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

export default defineConfig({
  plugins: [
    UnoCSS({
      // injectReset: '@unocss/reset/normalize.css', // 查看類型定義以瞭解所有包含的重置選項或如何傳入自己的選項
      // ...其他 Svelte 作用域選項
    }),
    sveltekit(),
  ],
})

添加配置文件

按照下面的描述設置你的 uno.config.ts 文件。

全局樣式

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

%unocss-svelte-scoped.global% 佔位符添加到你的 <head> 標籤中。在 Svelte 中,這是 index.html。在 SvelteKit 中,這將在 app.html%sveltekit.head% 之前:

html
<head>
  <!-- ... -->
  <title>使用 UnoCSS Svelte 作用域的 SvelteKit</title>
  %unocss-svelte-scoped.global%
  %sveltekit.head%
</head>

如果使用 SvelteKit,還必須在 src/hooks.server.js 文件的 transformPageChunk 鉤子中添加以下內容:

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
}

這個轉換必須在路徑包含 hooksserver 的文件中(例如 src/hooks.server.jssrc/hooks.server.ts),因為 svelte-scoped 將在你的服務器鉤子文件中查找以將 unocss_svelte_scoped_global_styles 替換為你的全局樣式。確保不要從另一個文件導入此轉換,例如在使用 @sveltejs/kit/hooks 中的 sequence 時。

在常規的 Svelte 項目中,Vite 的 transformIndexHtml 鉤子將自動執行此操作。

Svelte 預處理器

使用實用樣式構建一個不依賴於包含配套 CSS 文件的組件庫,通過使用預處理器將生成的樣式直接放入構建的組件中。查看 Stackblitz 中的 SvelteKit 庫示例

在 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

添加預處理器

在 Svelte 配置中添加 @unocss/svelte-scoped/preprocess

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

const config = {
  preprocess: [
    vitePreprocess(),
    UnoCSS({
      // ... 預處理器選項
    }),
  ],
  // 其他 Svelte 配置
}

開發環境中不要合併類名

在普通應用中使用 Svelte 作用域時,Vite 插件會自動檢測 devbuild。在開發環境中,類名將保持不同並就地哈希,以便在瀏覽器開發者工具中輕鬆切換開關。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 編譯器自動剝離。

作用域實用類釋放創造力

關於何時使用作用域樣式的一些建議:如果你已經到了項目生命週期中的這樣一個階段,每次使用像 .md:max-w-[50vw] 這樣的類(你知道它只使用一次)時,你就會感到全局樣式表不斷增大而感到不適,那麼就試試這個包吧。對於需要使用的確切類名的猶豫會抑制創造力。當然,你可以在樣式塊中使用 --at-apply: md:max-w-[50vw],但這變得乏味,而且上下文中的樣式很有用。此外,如果你想在項目中包含大量圖標,你會開始感受到將它們添加到全局樣式表的負擔。當每個組件承擔自身的樣式和圖標的重量時,你可以繼續擴展項目,而無需分析每個新增內容的成本收益。

配置

將你的 UnoCSS 設置放在 uno.config.ts 文件中:

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

export default defineConfig({
  // ...UnoCSS 選項
})

由於普通 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-預設無法工作。相反,請在 Svelte 作用域 Vite 插件之前使用 unplugin-attributify-to-class Vite 插件(attributifyToClass({ include: [/\.svelte$/] })
@unocss/preset-tagify-添加自定義提取器的預設將無法工作。創建一個預處理器將 <text-red>Hi</text-red> 轉換為 <span class="text-red">Hi</span>,然後創建一個 PR 在此處添加鏈接。

對於其他預設,如果它們不依賴於傳統的 class="..." 使用方式,你需要先將這些類名預處理為 class="..." 屬性。如果它們添加了像排版的 .prose 類這樣的預設,那麼你需要將觸發預設添加的類放入安全列表中。

轉換器支持

轉換器支持你的 CSS 文件(css|postcss|sass|scss|less|stylus|styl)。要使用它們,請在 vite.config.ts 中的 cssFileTransformers 選項中添加轉換器:

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

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

INFO

由於 Svelte 作用域的工作方式,轉換器不支持 Svelte 組件。

許可證

Released under the MIT License.