@fibrae/semi-ui-svelte 是 Semi Design Next 的 Svelte 组件库,面向 Svelte 5 应用,服务端渲染见服务端渲染。
环境要求
peer 依赖:svelte ^5.57.0。
| 项目 | 要求 |
|---|---|
| 浏览器 | Chrome 120、Firefox 125、Safari 17.2 及以上。支持 Safari 17.4 以下时需要导入 @fibrae/semi-polyfill,见下方「安装」 |
| 模块与语法 | 只发布 ESM,语法与内置 API 按 ESNext 原样发布,不降级 |
| Node.js | 24 及以上,服务端渲染会执行组件代码;Node 只运行构建插件与 semi-build-scss 时,22.17 及以上 |
| 构建插件 | Vite 8 及以上、webpack 5.77 及以上、rspack 2 |
安装
pnpm add svelte @fibrae/semi-ui-svelte @fibrae/semi-icons-svelte
图标包 @fibrae/semi-icons-svelte 是组件库的依赖,应用代码直接导入它时要在自己的 package.json 中声明,pnpm 不允许导入未声明的包。插画包 @fibrae/semi-illustrations-svelte 不随组件库安装,用到时单独安装。
Svelte 包发布的是 .svelte 源码与 .d.ts,不是编译产物:exports 带 svelte 条件,由 @sveltejs/vite-plugin-svelte(SvelteKit 已内置)按应用的目标(DOM 或服务端)编译。不识别 svelte 条件、也不编译 .svelte 的工具无法使用这些包。
使用 npm 或 yarn 时换成对应的命令。
需要支持 Safari 17.4 以下时,另装 @fibrae/semi-polyfill,并在应用入口的第一行导入(早于任何 Semi 组件)。它只补齐 AbortSignal.any 与 Element.prototype.checkVisibility,浏览器已有原生实现时什么都不做;只用到其中一项时可以导入 @fibrae/semi-polyfill/abort-signal-any 或 @fibrae/semi-polyfill/check-visibility。
import '@fibrae/semi-polyfill';
引入 token
组件会自己导入主题 token(--semi-* CSS 变量)与 base 规则,不引入也能正常显示。首个组件懒加载时,token 要等该组件的 chunk 加载后才生效;在应用入口文件最先导入 tokens 入口,token 就随入口的 CSS 一起加载,首屏的应用样式也能用上 --semi-* 变量。
Vite + Svelte 应用在入口文件导入:
// src/main.ts,放在其他导入之前
import '@fibrae/semi-ui-svelte/tokens';
SvelteKit 没有这样的入口文件,在根布局的脚本最前面导入:
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import '@fibrae/semi-ui-svelte/tokens';
let { children } = $props();
</script>
{@render children()}
tokens 入口是 JS 模块,导入的是组件所用的同一份 token,构建产物里只有一份,prefixCls 等构建插件选项对它同样生效。开启 omitCss 时它和组件样式一起被移除,token 由整包样式提供。
使用组件
<script lang="ts">
import { Button, Switch, Toast } from '@fibrae/semi-ui-svelte';
let checked = $state(false);
</script>
<Button theme="solid" type="primary" onclick={() => Toast.warning({ content: 'welcome' })}>Hello Semi</Button>
<Switch bind:checked aria-label="开关" />
受控值用 bind:checked={value} 双向绑定;只传 checked 时只作初值,没有 defaultChecked 一类的 default* 属性。单向传值时组件内交互改出的值是临时的:父级传入的值变化时以父级为准;同一组件上还有展开属性({...rest})时,展开对象换成新对象也会让父级的值重新生效。要保留交互结果,用 bind: 或在回调(如 onChange)里写回自己的状态。内容型属性(React 的 ReactNode 与 render 函数)是 snippet,写在组件标签内或用 {#snippet} 定义;style 只接受字符串,原生事件用小写名(onclick)。
也可以按目录深路径导入单个组件,路径写到 /index,如 import { Button } from '@fibrae/semi-ui-svelte/button/index';组件文件是 <目录>/<名>.svelte,如 @fibrae/semi-ui-svelte/button/button.svelte。
样式与 token 随组件按需加载,只有用到的组件会被打包。Semi 自带 box-sizing: border-box 与滚动条基础样式,不需要 reset.css 一类的重置样式,额外的重置样式会改变组件尺寸。
构建插件
使用默认前缀 semi、分层样式且由组件各自导入样式时,webpack、rspack 不需要构建插件。组件导入的是预编译的 CSS,类名前缀为 semi,包在 @layer semi 中:需要有处理 .css 导入的规则,如 webpack 的 css-loader 加 style-loader(或 mini-css-extract-plugin 的 loader)、rspack 的 type: 'css'。Vite 的开发服务器可以不用插件,但 vite build 需要 @fibrae/semi-vite-plugin,原因见下。服务端渲染的宿主另见服务端渲染。
需要下列能力时,使用对应构建工具的插件:Vite 用 @fibrae/semi-vite-plugin,webpack 5 用 @fibrae/semi-webpack-plugin,rspack 2 用 @fibrae/semi-rspack-plugin。插件对组件库、图标包与 AI 包都生效。
| 选项 | 默认值 | 说明 |
|---|---|---|
prefixCls | 'semi' | 类名前缀,同时改写组件 JS 中的前缀与编译出的样式 |
cssLayer | true | 样式是否包在 @layer semi 中,false 输出不分层的样式 |
omitCss | false | 去掉 Semi 包内的样式导入,改由应用加载整包样式,见整包样式 |
// vite.config.ts
import { defineConfig } from 'vite';
import semi from '@fibrae/semi-vite-plugin';
export default defineConfig({
plugins: [semi({ prefixCls: 'my' })],
});
// webpack.config.mjs
import SemiWebpackPlugin from '@fibrae/semi-webpack-plugin';
export default {
plugins: [new SemiWebpackPlugin({ prefixCls: 'my' })],
};
// rspack.config.mjs,项目需要有把 .scss 当作 CSS 处理的规则
import { SemiRspackPlugin } from '@fibrae/semi-rspack-plugin';
export default {
module: { rules: [{ test: /\.s?css$/, type: 'css' }] },
plugins: [new SemiRspackPlugin({ prefixCls: 'my' })],
};
webpack 插件另有 extractCssOptions(如改用 mini-css-extract-plugin 的 loader)与 overrideStylesheetLoaders(在默认 loader 链上追加自己的 loader)。vite dev 中类名仍是 semi-* 时,预构建缓存来自修改前的配置,用 vite --force 重新预构建。
Svelte 项目的构建配置有以下区别:
- 只有 Vite 路线:Svelte 包由
@sveltejs/vite-plugin-svelte(SvelteKit 已内置)编译。@fibrae/semi-webpack-plugin、@fibrae/semi-rspack-plugin不处理 Svelte 包,不要用于 Svelte 项目。 @fibrae/semi-vite-plugin对@fibrae/semi-ui-svelte、@fibrae/semi-ai-svelte与@fibrae/semi-icons-svelte生效,prefixCls、cssLayer、omitCss的含义不变,写成plugins: [svelte(), semi()];SvelteKit 见服务端渲染。
Semi 发布的 .scss 使用普通 Sass 无法解析的 semi: 地址,请通过构建插件或 @fibrae/semi-scss-compile 编译。
整包样式
需要一次性加载全部组件样式时,导入整包样式并开启构建插件的 omitCss,否则组件自带的样式导入会被再打包一份,同一组件的样式出现两次。整包样式已含 token 与图标样式,类名前缀为 semi,包在 @layer semi 中。
import '@fibrae/semi-ui-svelte/styles.css';
// 使用 AI 组件时,在其后导入
import '@fibrae/semi-ai-svelte/styles.css';
omitCss 时插件不再编译样式,cssLayer 不起作用,webpack、rspack 插件同时传入会报错。需要自定义前缀或不分层的整包样式时,用 @fibrae/semi-scss-compile 生成:--prefix 与插件的 prefixCls 一致,--no-layer 输出不分层的样式。-f、-i 可省略,缺省使用 @fibrae/semi-scss-compile 自己依赖的 foundation 与 icons-core,所以它的版本要与组件库一致;版本不一致时用 -f、-i 指向应用里安装的 @fibrae/semi-foundation、@fibrae/semi-icons-core 的 dist,pnpm 下这两个包要加为直接依赖。--ai 必须给出路径,指向 @fibrae/semi-ai-foundation 的 dist,pnpm 下要把它加为直接依赖,版本与组件库相同。
pnpm add -D @fibrae/semi-scss-compile
pnpm exec semi-build-scss -o src/semi.css --min --prefix my
# 使用 AI 组件时另生成一份,在上面那份之后导入(需再安装 @fibrae/semi-ai-foundation)
pnpm add -D @fibrae/semi-ai-foundation
pnpm exec semi-build-scss --ai node_modules/@fibrae/semi-ai-foundation/dist -o src/semi-ai.css --min --prefix my
定制样式
样式定制不需要构建插件:在应用的样式中覆写 --semi-* CSS 变量即可改变全局的颜色、圆角、字号、间距等,变量清单见设计变量,暗色模式的取值见暗色模式。Semi 的样式位于 @layer semi 中,应用中不分层的 CSS 规则总是优先,可以直接覆盖某个组件的具体样式,不需要 !important。
:root {
--semi-color-primary: var(--semi-violet-5);
}
.my-toolbar .semi-button {
border-radius: 0;
}
与 Tailwind 同用时,层的声明顺序与原子类中使用 Semi token 的方式见 Tailwind。
图标与插画
<script lang="ts">
import { IconHome } from '@fibrae/semi-icons-svelte';
</script>
<IconHome aria-label="首页" />
自定义 SVG 使用默认导出 Icon 的 children snippet,参数带上 fill:
<script lang="ts">
import Icon from '@fibrae/semi-icons-svelte';
</script>
<Icon size="large">
{#snippet children({ fill })}
<svg viewBox="0 0 24 24" {fill}><circle cx="12" cy="12" r="10" /></svg>
{/snippet}
</Icon>
插画从 @fibrae/semi-illustrations-svelte 导入,如 IllustrationNoContent。
图标样式随图标组件加载。图标列表与用法见图标。
AI 组件
AIChatInput、AIChatDialogue、Chat、Sidebar 等 AI 组件在独立的 AI 包中,基于组件库,样式随组件加载;加载整包样式时,AI 包的整包样式在组件库的之后导入。能力介绍见 AI 组件。
pnpm add @fibrae/semi-ai-svelte
@fibrae/semi-ai-svelte 目前只完成了 AIChatDialogue,AIChatInput、Chat、Sidebar 在移植中,尚未导出。
服务端渲染
服务端渲染与 hydration 按下表配置宿主,多数宿主需要加上 Semi 的构建插件。默认按组件加载样式:组件 JS 自带样式导入,不导入 styles.css。整包样式是可选路线:构建插件开 omitCss,再导入一次 styles.css,见整包样式。两条路线不混用,混用时 CSS 体积约翻倍。
| 宿主 | 安装 | 宿主配置 | 样式 |
|---|---|---|---|
| SvelteKit 3 | @fibrae/semi-ui-svelte、-D @fibrae/semi-vite-plugin | plugins: [sveltekit({ adapter }), semi()] | 无 |
// vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import semi from '@fibrae/semi-vite-plugin';
// adapter 换成你使用的 SvelteKit adapter
export default defineConfig({
plugins: [sveltekit({ adapter }), semi()],
});
组件在 svelte/server 的 render 与 SvelteKit 中输出服务端 HTML,浏览器用 hydrate 注水。组件内的 id 来自 $props.id(),服务端与浏览器一致;onMount 与 $effect 不在服务端执行,依赖它们的状态(如测量尺寸)在注水后才出现。
共同约定:
- 语言包从 UI 包的
locale/source/子路径导入(如locale/source/en_US),不需要直接安装@fibrae/semi-foundation。 semiGlobal只在应用启动时设置,按请求变化的配置用ConfigProvider。- 默认打开的弹层在 hydration 后出现。
- CodeHighlight 不内置语言:应用要直接安装
shiki(pnpm 不允许导入未声明的包),用CodeHighlight.register或languages传入shiki/langs/*的语言,见 CodeHighlight。