完成 Muse Studio React SPA 基座和六个用户端功能域,接入 MSW Mock、Tiptap 写作台、知识库资料管理、智能体槽位预检绑定、市场授权安装和个人中心用量权益视图。
77 lines
3.2 KiB
Markdown
77 lines
3.2 KiB
Markdown
# 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 属性定义:
|
||
```typescript
|
||
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`。应采用动态属性扩展或先判断再赋值:
|
||
```typescript
|
||
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]` 并强制中断。
|
||
- **解决方案**:
|
||
1. **本地开发**:在子项目根目录下创建 `.npmrc` 并添加以下白名单配置:
|
||
```ini
|
||
only-built-dependencies[]=esbuild
|
||
only-built-dependencies[]=msw
|
||
```
|
||
2. **CI 或自动化编译**:如果依然受到全局白名单阻碍,可使用 `--ignore-scripts` 绕过依赖构建脚本检查:
|
||
```bash
|
||
pnpm install --ignore-scripts
|
||
```
|
||
|
||
### 3. MSW 2.0 模拟 SSE (Server-Sent Events)
|
||
|
||
- **实现**:MSW 2.0 支持使用 `ReadableStream` 模拟 SSE 流式推送。在 Mock 处理器中可通过 `TextEncoder` 写入符合 EventSource 格式的缓冲行,并在 headers 中指定 `text/event-stream`。
|
||
```typescript
|
||
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` 类型报错,建议统一使用柯里化方法传入泛型定义:
|
||
```typescript
|
||
import create from 'zustand';
|
||
export const useUIStore = create<UIState>()((set) => ({ ... }));
|
||
```
|