- 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>
391 lines
8.7 KiB
Markdown
391 lines
8.7 KiB
Markdown
# 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 专家 |
|