Files
ai-mcp-web-ui/_bmad/implementation-artifacts/spec-json-render-phase2.md
Claude AI 89955ab677 feat(spec): json-render Phase 2 - 中低优先级改进规格
- Intent: 4 个中低优先级改进
- Tasks: 5 个详细任务(Props 简化、$computed、createRenderer、watch、repeat)
- Code Map: 4 个文件变更
- 验收标准: 功能/技术/文档三维度

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-03-25 05:24:12 +00:00

391 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# json-render Phase 2: 中低优先级改进规格
**规格 ID**: `spec-json-render-phase2`
**版本**: `1.0`
**状态**: `待批准`
**创建日期**: `2026-03-25`
**迭代**: `2/3`
---
## Intent
本规格包含 json-render v0.15.0 迁移的**中低优先级改进**,这些改进不是架构迁移的核心,但能显著提升开发体验和代码质量:
1. **组件 Props 类型简化** - 使用 `json-shorthand-prop-types` 简化 Props 定义
2. **$computed 函数支持** - 在 JSON UI 中支持计算属性
3. **createRenderer API 采用** - 使用新的渲染器创建 API
4. **watch/repeat 属性支持** - 添加响应式和重复渲染能力
---
## Approach
### 阶段 1: Props 类型简化
使用 `@json-render/shorthand-prop-types` 简化组件 Props 定义:
```typescript
// 简化前
import { z } from 'zod';
const CardSchema = z.object({
title: z.string().optional(),
description: z.string().optional(),
children: z.array(z.any()).optional(),
});
type CardProps = z.infer<typeof CardSchema>;
// 简化后
import { s } from '@json-render/shorthand-prop-types';
const CardSchema = {
title: s.string.optional,
description: s.string.optional,
children: s.array.optional,
};
```
### 阶段 2: $computed 函数集成
`JSONUIProvider` 中添加 `functions` prop支持计算属性
```typescript
<JSONUIProvider
functions={{
formatDate: (date: string) => new Date(date).toLocaleDateString('zh-CN'),
formatCurrency: (amount: number) => ${amount.toFixed(2)}`,
pluralize: (count: number, singular: string, plural: string) =>
count === 1 ? singular : plural,
}}
>
<JsonRenderer spec={spec} />
</JSONUIProvider>
```
### 阶段 3: createRenderer 迁移
使用 `createRenderer` API 简化渲染器创建:
```typescript
// 简化前
const JsonRenderer = ({ spec }: { spec: JsonSpec }) => {
return (
<JSONUIProvider>
<ComponentRenderer spec={spec} />
</JSONUIProvider>
);
};
// 简化后
import { createRenderer } from '@json-render/core';
const JsonRenderer = createRenderer();
```
### 阶段 4: watch/repeat 支持
添加响应式和重复渲染能力:
```typescript
// watch - 响应式更新
const specWithWatch = {
type: 'text',
content: '$formatDate(data.createdAt)',
watch: ['data.createdAt'], // 依赖变化时重新渲染
};
// repeat - 列表渲染
const specWithRepeat = {
type: 'stack',
repeat: {
items: '$data.items', // 数据源
template: { // 每项的模板
type: 'card',
title: '$item.name',
},
},
};
```
---
## Tasks
### Task 1: 简化组件 Props 类型定义
**文件**: `frontend-v2/lib/json-render-catalog.tsx`
**操作**:
1. 移除 `z.infer` 类型推导
2. 使用简化类型直接定义 Props
3. 保持 Zod Schema 用于运行时验证
**代码变更**:
```typescript
// 当前代码
import { z } from 'zod';
export const CardSchema = z.object({
title: z.string().optional(),
description: z.string().optional(),
children: z.array(z.any()).optional(),
});
export type CardProps = z.infer<typeof CardSchema>;
// 简化后
export const CardSchema = z.object({
title: z.string().optional(),
description: z.string().optional(),
children: z.array(z.any()).optional(),
});
export type JsonRenderProps = {
title?: string;
description?: string;
children?: JsonSpec[];
};
```
**验收标准**:
- [ ] 组件 Props 类型定义简洁
- [ ] 运行时验证仍然有效
- [ ] TypeScript 类型检查通过
---
### Task 2: 在 JSONUIProvider 中添加 functions prop
**文件**: `frontend-v2/components/JsonRenderer.tsx`
**操作**:
1. 添加 `functions` prop 到 `JSONUIProvider`
2. 将 functions 传递给 `ComponentRenderer`
3. 在表达式求值时支持函数调用
**代码变更**:
```typescript
// JsonRenderer.tsx
interface JSONUIProviderProps {
children: React.ReactNode;
functions?: Record<string, (...args: any[]) => any>;
}
export const JSONUIProvider: React.FC<JSONUIProviderProps> = ({
children,
functions = {},
}) => {
const contextValue = useMemo(
() => ({ functions }),
[functions]
);
return (
<JsonRenderContext.Provider value={contextValue}>
{children}
</JsonRenderContext.Provider>
);
};
```
**验收标准**:
- [ ] 可以在 spec 中使用 `$formatDate()` 等函数
- [ ] 函数调用正确传递参数
- [ ] 函数执行错误有友好提示
---
### Task 3: 迁移到 createRenderer API
**文件**: `frontend-v2/components/JsonRenderer.tsx`
**操作**:
1. 使用 `createRenderer` 替换手动 Provider 包装
2. 简化组件结构
3. 保持向后兼容
**代码变更**:
```typescript
// 迁移前
export const JsonRenderer: React.FC<JsonRendererProps> = ({ spec }) => {
return (
<JSONUIProvider>
<ComponentRenderer spec={spec} />
</JSONUIProvider>
);
};
// 迁移后
import { createRenderer } from '@json-render/core';
export const JsonRenderer = createRenderer();
```
**验收标准**:
- [ ] 代码量减少
- [ ] 功能保持不变
- [ ] 现有组件正常工作
---
### Task 4: 添加 watch 属性支持
**文件**: `frontend-v2/lib/json-render-catalog.tsx`
**操作**:
1. 扩展 JsonSchema 类型,添加 `watch` 属性
2.`ComponentRenderer` 中实现 watch 逻辑
3. 使用 React hooks 监听依赖变化
**代码变更**:
```typescript
// 扩展类型
export interface WatchableSchema extends JsonSchema {
watch?: string[]; // 依赖路径
}
// ComponentRenderer 中的实现
export const ComponentRenderer: React.FC<RendererProps> = ({ spec }) => {
const watchDeps = spec.watch || [];
const [data, setData] = useState(contextData);
useEffect(() => {
// 监听依赖变化
const unsubscribe = watchDependencies(watchDeps, (newData) => {
setData(newData);
});
return unsubscribe;
}, [watchDeps]);
return <ConcreteComponent {...spec} data={data} />;
};
```
**验收标准**:
- [ ] watch 属性正确触发重新渲染
- [ ] 依赖路径支持嵌套访问
- [ ] 无内存泄漏
---
### Task 5: 添加 repeat 属性支持
**文件**: `frontend-v2/lib/json-render-catalog.tsx`
**操作**:
1. 扩展 JsonSchema 类型,添加 `repeat` 属性
2.`ComponentRenderer` 中实现 repeat 逻辑
3. 支持动态列表渲染
**代码变更**:
```typescript
// 扩展类型
export interface RepeatableSchema extends JsonSchema {
repeat?: {
items: string | any[]; // 数据源路径或数组
template: JsonSchema; // 每项的模板
key?: string; // 唯一键字段
};
}
// ComponentRenderer 中的实现
export const ComponentRenderer: React.FC<RendererProps> = ({ spec, data }) => {
if (spec.repeat) {
const items = typeof spec.repeat.items === 'string'
? get(data, spec.repeat.items)
: spec.repeat.items;
return (
<>
{items.map((item, index) => (
<JsonRenderer
key={spec.repeat.key ? item[spec.repeat.key] : index}
spec={spec.repeat.template}
data={{ ...data, item }}
/>
))}
</>
);
}
return <ConcreteComponent {...spec} />;
};
```
**验收标准**:
- [ ] repeat 属性正确渲染列表
- [ ] 支持嵌套路径访问
- [ ] key 属性正确工作
---
## Code Map
| 文件 | 变更类型 | 描述 |
|------|----------|------|
| `frontend-v2/lib/json-render-catalog.tsx` | 修改 | 简化 Props 类型,添加 watch/repeat 支持 |
| `frontend-v2/lib/json-render-registry.tsx` | 修改 | 更新组件注册方式 |
| `frontend-v2/components/JsonRenderer.tsx` | 修改 | 添加 functions prop迁移到 createRenderer |
| `frontend-v2/app/page.tsx` | 修改 | 更新使用方式 |
---
## 验收标准
### 功能验收
- [ ] 所有组件使用简化的 Props 类型
- [ ] $computed 函数在 spec 中可用
- [ ] watch 属性正确触发重新渲染
- [ ] repeat 属性正确渲染列表
### 技术验收
- [ ] TypeScript 类型检查通过
- [ ] 运行时验证仍然有效
- [ ] 性能无明显下降
- [ ] 无内存泄漏
### 文档验收
- [ ] 更新 JSON_RENDER_USAGE.md
- [ ] 添加 $computed 函数文档
- [ ] 添加 watch/repeat 使用示例
---
## 依赖关系
本规格依赖于 **Phase 1** 的完成:
- Phase 1 完成后,本规格可以独立实施
- 部分功能(如 watch/repeat可能需要 Phase 1 的基础设施
---
## 时间估算
| Task | 预估时间 |
|------|----------|
| Task 1: Props 简化 | 2 小时 |
| Task 2: $computed 集成 | 3 小时 |
| Task 3: createRenderer 迁移 | 2 小时 |
| Task 4: watch 支持 | 4 小时 |
| Task 5: repeat 支持 | 4 小时 |
| **总计** | **15 小时** |
---
## 批准记录
| 字段 | 值 |
|------|-----|
| 批准者 | |
| 批准日期 | |
| 批准理由 | |
---
## 变更历史
| 日期 | 版本 | 变更内容 | 作者 |
|------|------|----------|------|
| 2026-03-25 | 1.0 | 初始版本 | 外层 BMAD 专家 |