- 前端
- UI组件
- 设计系统
【免费下载链接】semi-design
🚀A modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.🎨Provide 3000+ Design Tokens, easy to build your design system. Make Semi Design to Any Design.🧑🏻💻 Design to Code in one click
本指南以仓库内 semi-playground-for-ai/AGENTS.md 为骨架,结合同一目录下的 README、Rspack 构建配置与源码,完整讲解这个面向 AI 开发与测试的 Semi Design 组件沙箱:如何用
npm run dev / build / preview三条命令驱动一个不经过 npm 包、直接编译packages/源码并支持热更新的开发环境,以及它背后的 alias 映射、React 19 源码切换 loader、SCSS Design Token 注入等实现细节。读完本文,你可以独立复现这套「改源码即热更新」的组件验证工作流,并理解其与 npm 包引用方式的行为差异。
一、项目定位:给 AI 的组件实验场
semi-playground-for-ai/README.md 开宗明义:这是一个「用于 AI 开发和测试 Semi Design 组件的 playground 项目」。与常规的 demo 项目不同,它的三大特性决定了它的技术选型:
- 直接引用外部仓库
packages/目录下的 Semi Design 源码,而不是从 npm 拉取构建好的产物; - 修改源码后实时热更新,无需重新编译,这使它天然适合 AI 在迭代组件时快速验证行为;
- 基于 Rspack 构建,启动快速,符合大仓库多包场景下对构建性能的要求。
AGENTS.md 则是写给进入该项目的 AI 代理的「工作守则」:声明 AI 应具备 JavaScript、Rspack 与 Web 应用开发能力,写出可维护、高性能、可访问的代码;随后给出三条核心命令并指向 Rspack 官方文档(rspack.rs/llms.txt,即面向 LLM 的机器可读文档)。
二、三条命令:dev / build / preview
AGENTS.md 中的命令与 package.json 的 scripts 一一对应,底层全部由@rspack/cli(当前仓库锁定^1.6.8)驱动:
| 命令 | 底层调用 | 用途 |
|---|---|---|
npm run dev | rspack dev | 启动开发服务器,支持热更新(HMR) |
npm run build | rspack build | 构建生产环境产物 |
npm run preview | rspack preview | 本地预览生产构建结果 |
完整的使用流程(源自 README):
# 安装依赖 npm install # 启动开发服务器 npm run dev开发服务器行为在 rspack.config.ts 的devServer段有明确定义:
port: 0:使用随机可用端口,避免多个沙箱实例端口冲突;hot: true:开启模块热更新;open: false:不自动打开浏览器;historyApiFallback: true:支持前端路由历史模式;static.directory指向public目录,提供静态资源。
生产构建则通过optimization段做了针对性优化:rspack.SwcJsMinimizerRspackPlugin压缩 JS,LightningCssMinimizerRspackPlugin压缩 CSS(minimizerOptions.targets与编译目标一致),并用splitChunks将react/react-dom单独拆成lib-reactchunk(priority 20)、其余 node_modules 归入vendors(priority 10);output.filename在开发环境为[name].js,生产环境带[name].[contenthash:8].js缓存指纹;performance对入口与资源设置 10MB 上限,生产模式超限会给出 warning。
三、核心机制:alias 直连源码,让「改源码即生效」
沙箱不依赖 npm 包,而是通过 Rspackresolve.alias把@douyinfe/*命名空间指向仓库内的真实源码目录。配置采用精确匹配与前缀匹配两级策略(见 rspack.config.ts):
// 精确匹配($)指向入口文件,避免读取 package.json 的 main/module 字段 "@douyinfe/semi-ui$": path.join(packagesDir, "semi-ui/index.ts"), "@douyinfe/semi-foundation$": path.join(packagesDir, "semi-foundation/index.ts"), "@douyinfe/semi-icons$": path.join(packagesDir, "semi-icons/src/index.ts"), "@douyinfe/semi-icons-lab$": path.join(packagesDir, "semi-icons-lab/src/index.tsx"), "@douyinfe/semi-illustrations$": path.join(packagesDir, "semi-illustrations/src/index.ts"), "@douyinfe/semi-animation$": path.join(packagesDir, "semi-animation/index.ts"), "@douyinfe/semi-animation-react$": path.join(packagesDir, "semi-animation-react/index.ts"), "@douyinfe/semi-animation-styled$": path.join(packagesDir, "semi-animation-styled/index.ts"), "@douyinfe/semi-json-viewer-core$": path.join(packagesDir, "semi-json-viewer-core/src/index.ts"), "@douyinfe/semi-theme-default$": path.join(packagesDir, "semi-theme-default/scss/index.scss"), // 前缀匹配用于深层导入 "@douyinfe/semi-ui": path.join(packagesDir, "semi-ui"), // ... 其余包同理这里有一个容易被忽略的关键设计:带$的精确匹配直接指向各包的index.ts入口文件,而不是让 Rspack 去解析package.json的main/module字段——这样既能保证入口确定性,也避免了「解析到 dist 产物」从而绕过源码的情况。前缀匹配(不带$)则覆盖深层的子路径导入,例如@douyinfe/semi-ui/es/xxx这类写法也能落到源码目录。
除此之外,alias 还做了两个「环境隔离」:
"react/jsx-runtime": path.join(require.resolve("react"), "..", "jsx-runtime.js"), "react-dom": path.dirname(require.resolve("react-dom/package.json")), "react": path.dirname(require.resolve("react/package.json")),即强制react/react-dom从 playground 自己的node_modules解析,避免 React 实例被多副本化(否则会引发 hooks 状态丢失、context 失效等经典双 React 问题)。
README 给出的「引用方式」也因此与 npm 包完全一致,用户无感知:
import { Button, Input, Select } from '@douyinfe/semi-ui'; import { IconSearch } from '@douyinfe/semi-icons';TypeScript 侧由 tsconfig.json 的paths做同样映射(同时支持精确与*通配子路径),并设置moduleResolution: "bundler"、jsx: "react-jsx"、strict: true;include覆盖["src", "../packages"],保证编辑器的类型检查也直达源码。
四、React 19 源码切换:semi-react19-loader
仓库的 loaders/semi-react19-loader.js 是一个自定义 webpack/Rspack loader,负责把semi-ui源码中的 React 18 写法切换为 React 19 写法。其原理依赖源码内的特殊注释标记:
REACT_18_START ... REACT_18_END:包裹 React 18 版本代码,loader 直接整块删除;REACT_19_START ... REACT_19_END:包裹 React 19 版本代码(默认处于注释状态),loader 取消注释并移除每行开头的//。
module.exports = function semiReact19Loader(source) { // 删除 REACT_18 代码块(包括标记) let result = source.replace( /\/\*\s*REACT_18_START\s*\*\/[\s\S]*?\/\*\s*REACT_18_END\s*\*\//g, '' ); // 取消注释 REACT_19 代码块 result = result.replace( /\/\*\s*REACT_19_START\s*\*\/([\s\S]*?)\/\*\s*REACT_19_END\s*\*\//g, (match, code) => code.replace(/^\s*\/\/\s?/gm, '') ); return result; };在 rspack.config.ts 中,semi-ui的 TS/JSX 文件会先经过semi-react19-loader,再进入builtin:swc-loader(注释写明 loader 自下而上执行,因此 react19-loader 先跑):
{ test: /\.(jsx?|tsx?)$/, include: [path.resolve(packagesDir, "semi-ui")], use: [ { loader: "builtin:swc-loader", options: { /* swc 配置 */ } }, path.resolve(__dirname, "loaders/semi-react19-loader.js"), ] }swc 编译配置中,jsc.transform.react采用runtime: "automatic"(React 17+ 的 JSX 转换),并依据argv.mode判断development与refresh——开发模式自动注入 React Refresh 运行时,这是热更新生效的关键一环。其他源码包(semi-foundation、semi-icons 等)则走第二条规则,仅用 swc-loader 编译,不经过该 loader。与此同时,仓库根目录下还保留了 packages/semi-ui/react19-adapter.ts,作为组件库自身面向 React 19 的适配层,与 playground 的 loader 方案互为补充。
五、Design Token 注入:SCSS 编译与 npm 包行为对齐
Semi Design 的样式体系依赖 SCSS 变量与 CSS 变量。为了让源码直编的行为与「npm 包」一致,rspack.config.ts 的 sass-loader 通过additionalData做了三处注入:
const themeDir = path.resolve(packagesDir, "semi-theme-default/scss").replace(/\\/g, '/'); const scssVarStr = `@import "${themeDir}/index.scss";\n`; // SCSS 变量 const animationStr = `@import "${themeDir}/animation.scss";\n`; // 动画 const cssVarStr = `@import "${themeDir}/global.scss";\n`; // CSS 变量 // 只在 _base/base.scss 中注入 CSS 变量(跟 npm 包行为一致) if (/_base[\\/]base\.scss/.test(loaderContext.resourcePath)) { return scssVarStr + animationStr + cssVarStr + content; } return scssVarStr + animationStr + content;要点如下:
- 每个 SCSS 文件都会被注入
semi-theme-default/scss/index.scss(Design Token 的 SCSS 变量)与animation.scss,保证任意组件的样式文件都能引用到 token 变量; - 只有
_base/base.scss额外注入global.scss(CSS 变量),这是因为 Semi 的 npm 包只在基础样式层输出 CSS 变量(对应源码位置为 packages/semi-foundation/_base/base.scss 与 packages/semi-theme-default/scss 目录)——沙箱刻意复刻了这一行为; sassOptions.includePaths加入semi-theme-default/scss与semi-foundation,让@import能够按 Semi 的原始约定解析变量;- 模块规则以
type: "css/auto"输出样式,配合experiments.css: true,由 Rspack 原生处理 CSS 的提取与热更新。
对应地,src/main.tsx 中特别注释强调「不需要手动导入 global.scss!CSS 变量会自动通过_base/base.scss注入」,这正解释了上述机制对使用方的影响。
六、实战示例:在沙箱中验证 AIChatInput
沙箱自带一个真实测试用例(src/App.tsx),主题是 AI 组件AIChatInput的「仅技能时的 placeholder 展示」行为,正好契合本沙箱面向 AI 场景的定位:
import { AIChatInput } from '@douyinfe/semi-ui'; import { useRef, useState } from 'react'; // 从 constant.jsx 里拿的测试用 skills const skills = [ { key: 'code', label: '代码生成', value: '/代码生成', hasTemplate: true }, { key: 'translate', label: '翻译', value: '/翻译', hasTemplate: false }, { key: 'summarize', label: '总结', value: '/总结', hasTemplate: true }, { key: 'write-email', label: '写邮件', value: '/写邮件', hasTemplate: false }, ];它覆盖了三个断言场景:
- 开启
showPlaceholderWhenSkillOnly={true}时,只有技能(无输入内容)也应显示 placeholder; - 不开启该属性(默认行为)时,仅技能不显示 placeholder;
- 通过
defaultContent预置了默认技能(${skills[0].value})时,期望仍显示 placeholder。
入口 index.html 提供#root挂载点,src/main.tsx 使用ReactDOM.createRoot渲染<App />。你可以在此基础上替换为自己的组件用例,验证逻辑后执行npm run dev在浏览器中确认表现。
七、构建模式判别与整体工作流
rspack.config.ts 使用函数形式的defineConfig((env, argv) => ...),通过argv.mode === "development"可靠区分构建模式,从而联动devtool(开发环境cheap-module-source-map,生产环境source-map)、minimize、filename指纹、performance.hints(开发模式关闭提示)以及ReactRefreshPlugin的挂载(isDev && new ReactRefreshPlugin(),配合plugins数组末尾的.filter(Boolean)剔除假值)。browserslist 目标为["last 2 versions", "> 0.2%", "not dead", "Firefox ESR"]。
综合来看,该沙箱的完整工作流是:
npm install安装 playground 自身的依赖(React 18、Rspack、TypeScript 等,见 package.json);- 修改
packages/下任意 Semi Design 包的源码、SCSS 或组件; - 运行
npm run dev,alias 直连源码 + HMR 使改动即时生效,无需编译产物; - 复用
@douyinfe/*命名空间编写用例,验证组件在真实源码下的行为; - 需要交付验证时用
npm run build产出带缓存指纹的生产包,npm run preview本地预览。
结语与适用场景
semi-playground-for-ai 的价值在于把「Semi Design 组件源码迭代」与「AI 快速验证」衔接起来:AI 代理依据 AGENTS.md 的约定进入项目,用三条命令即可完成开发、构建与预览闭环;而仓库的 alias 直连、react19-loader 与 SCSS token 注入三套机制,保证了「源码即真身」与「行为同 npm 包」的一致性。这一模式同样适用于其他需要频繁改动底层组件库源码、又想获得秒级反馈的 monorepo 场景——如需了解其姊妹项目,可对比 semi-live-for-ai 基于 React Live 的在线编辑器方案。
- 前端
- UI组件
- 设计系统
【免费下载链接】semi-design
🚀A modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.🎨Provide 3000+ Design Tokens, easy to build your design system. Make Semi Design to Any Design.🧑🏻💻 Design to Code in one click
相关推荐
Mastering Semi Design CodeHighlight:基于 Prism 的 297 语言代码高亮组件实战与源码解析
Mastering Semi Design CodeHighlight:基于 Prism 的 297 语言代码高亮组件实战与源码解析 Semi Design 的
前端UI组件设计系统Semi Design Anchor 锚点组件开发指南:从基础用法到源码级原理剖析
Semi Design Anchor 锚点组件开发指南:从基础用法到源码级原理剖析 Anchor(锚点)是 Semi Design React UI 库中用于构
前端UI组件设计系统Rspack热更新原理:提升开发效率的黑科技
Rspack热更新原理:提升开发效率的黑科技 在现代化的前端开发中, Rspack热更新 技术已经成为提升开发效率的关键利器。作为基于Rust构建的高性能构建工
开发工具前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考