完成 Muse Studio React SPA 基座和六个用户端功能域,接入 MSW Mock、Tiptap 写作台、知识库资料管理、智能体槽位预检绑定、市场授权安装和个人中心用量权益视图。
3.2 KiB
3.2 KiB
muse-studio 脚手架与核心基础设施搭建留痕
任务背景
在根目录从零初始化前端用户端子项目 muse-studio(Vite + React + TS 结构),并完成路由、统一客户端、MSW 2.0 Mock、IndexedDB 及 Zustand 状态库等基础设施建设。
关键工程实践与避坑指南
1. TypeScript 6.0 严格模式兼容
(1) erasableSyntaxOnly 限制
- 现象:当启用该规则时,TypeScript 6.0 禁用了 parameter properties 语法(如在 constructor 参数中直接写
public code: string)。 - 避坑:声明异常类或常规类时,需采用标准 ES ES6 属性定义:
export class ApiError extends Error { public code: string; public status: number; constructor(code: string, message: string, status: number) { super(message); this.code = code; this.status = status; } }
(2) exactOptionalPropertyTypes 限制
- 现象:在对象字面量中如果显式将可选属性设为
undefined会报错。 - 避坑:构造请求参数(如 RequestInit 的
body)时,避免传入body: undefined。应采用动态属性扩展或先判断再赋值:const options: RequestInit = { method: 'POST' }; if (data !== undefined) { options.body = JSON.stringify(data); }
(3) baseUrl 被废弃
- 现象:TypeScript 6.0+ 将逐渐废弃
baseUrl,编译时会报 TS5101 错误。 - 避坑:从
tsconfig.app.json中直接移除"baseUrl",Vite + TS 依然能够通过相对位置正确解析"paths"(别名映射)。
2. pnpm 包管理构建脚本受阻
- 现象:在新版 pnpm (v10/v11) 中,若存在未显式授权的构建脚本(如
esbuild,msw),任何 pnpm 命令在运行依赖完整性检查时都会抛出[ERR_PNPM_IGNORED_BUILDS]并强制中断。 - 解决方案:
- 本地开发:在子项目根目录下创建
.npmrc并添加以下白名单配置:only-built-dependencies[]=esbuild only-built-dependencies[]=msw - CI 或自动化编译:如果依然受到全局白名单阻碍,可使用
--ignore-scripts绕过依赖构建脚本检查:pnpm install --ignore-scripts
- 本地开发:在子项目根目录下创建
3. MSW 2.0 模拟 SSE (Server-Sent Events)
- 实现:MSW 2.0 支持使用
ReadableStream模拟 SSE 流式推送。在 Mock 处理器中可通过TextEncoder写入符合 EventSource 格式的缓冲行,并在 headers 中指定text/event-stream。http.get('/api/ai/stream', () => { const encoder = new TextEncoder(); const stream = new ReadableStream({ async start(controller) { controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'chunk', content: '数据' })}\n\n`)); controller.close(); } }); return new HttpResponse(stream, { headers: { 'Content-Type': 'text/event-stream' } }); })
4. Zustand 4 严格模式类型推断
- 避坑:在 Zustand Store 中,为了避免隐式
any类型报错,建议统一使用柯里化方法传入泛型定义:import create from 'zustand'; export const useUIStore = create<UIState>()((set) => ({ ... }));