关于 plan-ui-change
用于规划和实现复杂 Blazor UI 改动的技能,帮助编码 Agent 将界面拆分为职责明确的组件,确定状态与数据流,并从叶子组件开始构建。
主要功能
- 01将界面区域映射为组件
- 02划分渲染模式和状态归属
- 03定义参数与事件的数据流
- 04检查组件复用机会
- 05自下而上实现组件
适用场景
- 01规划包含多个区域的仪表板
- 02将大型页面拆分为组件
- 03组织父子组件的状态与事件
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dotnet/ skills --skill plan-ui-change安装后在 Claude Code 中输入/plan-ui-change确认已加载。
详细介绍
规划 Blazor UI 更改
当收到构建复杂 UI 功能的请求时,先规划组件拆分,然后立即开始实现。单体页面组件几乎从来不是正确的做法——应将 UI 拆分为职责明确、可组合的组件。
规划工作流
第 1 步 — 梳理视觉区域
阅读请求,找出每个不同的视觉区域。每个拥有自身数据、行为或布局职责的区域,都可以考虑拆分为一个组件。
绘制组件树:
InventoryDashboard (page — owns data, orchestrates layout)
├── StockSummaryBar (read-only stats: total items, low-stock count, value)
├── InventoryFilters (search box, category dropdown, stock-level toggle)
├── InventoryTable (sortable table of products)
│ └── InventoryRow (single product row with inline edit/delete)
└── AddProductForm (slide-out form for new products)
识别组件的规则:
- 职责明确 — 某个区域拥有自己的状态或行为 → 拆分为单独的组件
- 结构重复 — 列表中的项目、网格中的卡片 → 提取项目模板
- 交互独立 — 某个区域独立于同级区域处理用户输入 → 拆分为单独的组件
- 尺寸 — 任何单独就会超过约 150 行标记的区域 → 拆分
第 2 步 — 对各组件进行分类
对于组件树中的每个组件,确定:
| 组件 | 操作 | 渲染模式 | 拥有的状态 | 行数(估计) |
|---|---|---|---|---|
| InventoryDashboard | 创建 | InteractiveServer | 产品列表、筛选状态 | ~80 |
| StockSummaryBar | 创建 | (继承) | 无 — 接收数据 | ~30 |
| InventoryFilters | 创建 | (继承) | 搜索文本、所选类别 | ~60 |
| InventoryTable | 创建 | (继承) | 排序列、排序方向 | ~50 |
| InventoryRow | 创建 | (继承) | 行内编辑模式标志 | ~60 |
| AddProductForm | 创建 | (继承) | 表单模型 | ~80 |
页面组件的标记和代码总量超过约 200 行,就太大了。 如果估算后发现单个组件超过这个规模,就应进一步拆分。
第 3 步 — 设计数据流
确定每项数据的状态所有者,然后梳理数据流:
InventoryDashboard (owns: products[], filters)
│
├─ [Parameter] products ──→ StockSummaryBar (reads aggregate stats)
│
├─ [Parameter] filters ──→ InventoryFilters
│ └─ EventCallback<Filters> OnFiltersChanged ──→ InventoryDashboard
│
├─ [Parameter] filteredProducts ──→ InventoryTable
│ └─ [Parameter] product ──→ InventoryRow
│ ├─ EventCallback<Product> OnSave ──→ InventoryTable ──→ InventoryDashboard
│ └─ EventCallback<Product> OnDelete ──→ InventoryTable ──→ InventoryDashboard
│
└─ EventCallback<Product> OnProductAdded ←── AddProductForm
规则:
- 数据始终通过
[Parameter]向下流动 - 事件始终通过
EventCallback<T>向上传递 - 页面/父组件拥有数据,并将筛选或转换后的视图传递给子组件
- 子组件不得修改参数——应通过回调通知父组件
- 如果数据必须跨越 2 个以上层级,而中间组件又不需要该数据,则使用级联值或作用域服务
第 4 步 — 识别复用机会
创建新组件之前,先检查项目中是否已有可用于此目的的组件。查找:
- 结构相符的现有列表项组件
- 项目中已有的共享筛选/搜索组件
- 接受模板的通用组件(例如
DataTable<T>、Pagination)
如果某个组件会用于多个页面,请将其放在 Shared/ 或 Components/ 文件夹中。
第 5 步 — 安排实现顺序
从底向上构建——先实现叶子组件,再实现组合它们的父组件:
- 模型/DTO — 定义数据结构
- 服务 — 数据访问、业务逻辑(接口 + 实现)
- 叶子组件 — 没有子组件的组件(InventoryRow、StockSummaryBar)
- 容器组件 — 组合叶子组件的组件(InventoryTable、InventoryFilters)
- 页面组件 — 串联所有内容并注册路由
- 配置 — DI 注册、渲染模式设置
每个组件都应能独立编译。绝不要引用尚不存在的组件。
输出格式
简要展示计划,然后立即开始实现——绝不要只停留在计划阶段,也不要在编写代码前请求确认。计划是思考工具,不是交付内容。
## Component Plan: [Feature Name]
### Component Tree
[ASCII tree showing parent-child relationships]
### Component Table
| Component | Action | Render Mode | Purpose | Est. Lines |
|-----------|--------|-------------|---------|------------|
| ... | ... | ... | ... | ... |
### Data Flow
[State owner] → [Parameters down] → [EventCallbacks up]
### Implementation Order
1. [First file to create — why]
2. [Second file — why]
...
输出计划后,立即按列出的顺序开始实现组件。不要等待批准,也不要询问“要继续吗?”——计划是供你遵循的指南,不是等待用户批准的提案。
应避免的反模式
| 反模式 | 错误原因 | 正确做法 |
|---|---|---|
| 一个页面组件超过 500 行 | 无法测试、复用或维护 | 拆分为职责明确的组件 |
| 通过中间组件传递 10 个以上的参数 | 参数层层传递会模糊意图 | 使用级联值或作用域状态服务 |
| 子组件从 API 获取自己的数据 | 多个组件会发起重复请求 | 由父组件管理数据,并通过参数传递 |
| 使用复杂标记内联渲染列表项 | 逻辑重复、无法复用且难以测试 | 将项模板提取为独立组件 |
| 先在一个文件中构建所有内容,再“稍后重构” | 重构很少真正发生;最终交付的仍是单体 | 提前规划拆分方案 |
| 为一次性用途构建通用组件 | 过度设计会增加复杂性 | 只有在确认存在复用需求时才提取泛型 |
指南
- 简要规划,然后立即实现。 编写简洁的组件表和数据流图,然后立即创建
.razor文件——不要只停留在规划阶段。 - 优先选择多个小组件,而非一个大组件。 职责明确的组件更易于理解、测试和复用。
- 首先决定状态归属。 编写数据获取逻辑之前,先确定由哪个组件管理数据。
- 自底向上构建。 先创建叶子组件,这样父组件就能立即引用它们。
- 根据组件渲染的内容命名,而非其内部执行的操作:使用
ProductCard,而非ProductRenderer;使用OrderFilters,而非FilterHandler。
评论
支持 Markdown