跳到内容

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 文件更方便。对于 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 哈希方法,而是使用基于文件名 + 类名(s)的哈希来编译唯一的类名,这些类名可以全局化而不会出现样式冲突。

用法

由于 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> 块中或使用 attributify 模式,那么您需要在使用 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
}

此转换必须位于 路径包含 hooksserver 的文件中(例如 src/hooks.server.jssrc/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 插件会自动检测 devbuild。在开发中,类将保持独立并就地哈希,以便在浏览器的开发者工具中轻松打开/关闭。class="mb-1 mr-1" 将变成类似 class="_mb-1_9hwi32 _mr-1_84jfy4 的内容。在生产中,这些类将使用您所需的默认前缀 uno- 和基于文件名 + 类名的哈希编译成单个类名,例如 class="uno-84dke3

如果您希望在使用预处理器时获得相同的行为,则必须手动根据环境设置 combine 选项。一种方法是安装 cross-env 并将您的 dev 脚本更新为此脚本

"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="..."属性中。如果它们添加了像排版.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 许可证发布。