ui
Vite + React 19 + TypeScript + Tailwind v4 + Alova,项目结构遵循 Feature-Sliced Design v2.1。
命令
bun run dev # 启动开发服务器
bun run build # tsc -b && vite build
bun run lint # oxlint
bun run lint:fsd # Steiger —— 官方 FSD 结构校验
bun run gen:api # 依据 openapi.json 重新生成 src/shared/api
目录结构
当前采用最小可用分层(app + pages + shared)。features/、entities/ 只在
「同一段代码被多处实际使用且边界稳定」时才创建,不要提前建空目录;
widgets/ 层不推荐采用。
src/
app/ ← 应用初始化:入口、路由、全局样式、providers
index.tsx ← 入口(index.html 指向这里)
routes/router.tsx ← 路由表,唯一允许 import pages 的地方
shell/providers.tsx ← 全局 Provider 组合(Redux / QueryClient / theme…)
styles/index.css ← 全局样式(Tailwind 入口)
pages/ ← 路由级组合,页面自己拥有的逻辑都留在这里
home/
ui/home-page.tsx
index.ts ← 切片公共 API
not-found/
ui/not-found-page.tsx
index.ts
shared/ ← 无业务逻辑的基础设施,按 segment 划分、无 slice
ui/ ← UI kit(Button…),公共 API 在 ui/index.ts
api/ ← Alova 实例与 OpenAPI 生成代码(勿手改,见 gen:api)
约定
-
导入方向:
app → pages → widgets → features → entities → shared,只能向下依赖, 同层 slice 之间禁止互相 import。 -
公共 API:外部只能通过 slice 的
index.ts(shared 则是每个 segment 的index.ts)导入。import { Button } from '@/shared/ui' // ✅ import { Button } from '@/shared/ui/button' // ❌ 绕过公共 API -
路径别名:
@/app@/pages@/widgets@/features@/entities@/shared在tsconfig.app.json与vite.config.ts两处保持同步。 -
非 auth 跳转一律走 react-router:auth 流程之外的页面禁止
<a href>与window.location整页跳转,只用Link/useNavigate,保证 SPA 内最小化切换 (scripts/lint-nav.mjs强制,已并入bun run lint)。 -
文件命名按业务域,不用技术角色:
model/user.ts而不是model/types.ts。 -
shared 不含业务逻辑;CRUD 与鉴权 token 属于
shared/api/shared/auth, 不要为此新建userentity。 -
Pages first:拿不准放哪里就留在页面里,重复不是下沉的充分条件。
新增页面:建 pages/<name>/ui/<name>-page.tsx + index.ts,再到
src/app/routes/router.tsx 注册路由。
注意(Windows + WSL)
工作区通过 \\wsl.localhost\... 访问时,bun 无法回写 bun.lock,Windows 侧 node
也解析不了部分包的入口(vite build 会报 plugin-react 入口错误)。
安装依赖与运行 dev / build / lint:fsd 建议在 WSL 内执行。