mcpField
P
plan-ui-change

plan-ui-change

github.com/dotnet开发5.8k3.2k 次安装1 天前更新
代码仓库

关于 plan-ui-change

用于规划和实现复杂 Blazor UI 改动的技能,帮助编码 Agent 将界面拆分为职责明确的组件,确定状态与数据流,并从叶子组件开始构建。

主要功能

  1. 01将界面区域映射为组件
  2. 02划分渲染模式和状态归属
  3. 03定义参数与事件的数据流
  4. 04检查组件复用机会
  5. 05自下而上实现组件

适用场景

  1. 01规划包含多个区域的仪表板
  2. 02将大型页面拆分为组件
  3. 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 步 — 安排实现顺序

从底向上构建——先实现叶子组件,再实现组合它们的父组件:

  1. 模型/DTO — 定义数据结构
  2. 服务 — 数据访问、业务逻辑(接口 + 实现)
  3. 叶子组件 — 没有子组件的组件(InventoryRow、StockSummaryBar)
  4. 容器组件 — 组合叶子组件的组件(InventoryTable、InventoryFilters)
  5. 页面组件 — 串联所有内容并注册路由
  6. 配置 — 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