# 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; // 简化后 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 new Date(date).toLocaleDateString('zh-CN'), formatCurrency: (amount: number) => `¥${amount.toFixed(2)}`, pluralize: (count: number, singular: string, plural: string) => count === 1 ? singular : plural, }} > ``` ### 阶段 3: createRenderer 迁移 使用 `createRenderer` API 简化渲染器创建: ```typescript // 简化前 const JsonRenderer = ({ spec }: { spec: JsonSpec }) => { return ( ); }; // 简化后 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; // 简化后 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 any>; } export const JSONUIProvider: React.FC = ({ children, functions = {}, }) => { const contextValue = useMemo( () => ({ functions }), [functions] ); return ( {children} ); }; ``` **验收标准**: - [ ] 可以在 spec 中使用 `$formatDate()` 等函数 - [ ] 函数调用正确传递参数 - [ ] 函数执行错误有友好提示 --- ### Task 3: 迁移到 createRenderer API **文件**: `frontend-v2/components/JsonRenderer.tsx` **操作**: 1. 使用 `createRenderer` 替换手动 Provider 包装 2. 简化组件结构 3. 保持向后兼容 **代码变更**: ```typescript // 迁移前 export const JsonRenderer: React.FC = ({ spec }) => { return ( ); }; // 迁移后 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 = ({ spec }) => { const watchDeps = spec.watch || []; const [data, setData] = useState(contextData); useEffect(() => { // 监听依赖变化 const unsubscribe = watchDependencies(watchDeps, (newData) => { setData(newData); }); return unsubscribe; }, [watchDeps]); return ; }; ``` **验收标准**: - [ ] 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 = ({ 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) => ( ))} ); } return ; }; ``` **验收标准**: - [ ] 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 专家 |