
关于
将 SwiftUI 视图重构为更小的组件,确保稳定、显式的数据流。
name: swiftui-view-refactor description: 将 SwiftUI 视图重构为更小的组件,确保稳定、显式的数据流。 risk: safe source: "Dimillian/Skills (MIT)" date_added: "2026-03-25"
SwiftUI 视图重构
概述
将 SwiftUI 视图重构为小型、显式、稳定的视图类型。默认使用原生 SwiftUI:视图中的本地状态、环境中的共享依赖、服务/模型中的业务逻辑,仅在请求或现有代码明确需要时才使用视图模型。
使用场景
- 清理大型 SwiftUI 视图或拆分过长的
body实现时。 - 需要更小的子视图、显式依赖注入或更好的 Observation 使用时。
核心准则
1) 视图排序(从上到下)
- 除非现有文件有更强的本地约定需要保留,否则强制执行此排序。
- Environment
private/publiclet@State/ 其他存储属性- 计算
var(非视图) initbody- 计算视图构建器 / 其他视图辅助方法
- 辅助 / 异步函数
2) 默认使用 MV,而非 MVVM
- 视图应该是轻量级的状态表达和编排点,而非业务逻辑的容器。
- 优先使用
@State、@Environment、@Query、.task、.task(id:)和onChange,再考虑视图模型。 - 通过
@Environment注入服务和共享模型;将领域逻辑保留在服务/模型中,而非视图 body 中。 - 不要仅仅为了镜像本地视图状态或包装环境依赖而引入视图模型。
- 如果屏幕变得庞大,先将 UI 拆分为子视图,再考虑创建新的视图模型层。
3) 强烈优先使用专用子视图类型,而非计算 some View 辅助属性
- 标记超过大约一屏或包含多个逻辑部分的
body属性。 - 优先为非平凡部分提取专用
View类型,特别是当它们有状态、异步工作、分支或值得拥有自己的预览时。 - 保持计算
some View辅助属性少而小。不要用private var header: some View风格的片段构建整个屏幕。 - 向提取的子视图传递小型、显式的输入(数据、绑定、回调),而非传递整个父状态。
- 如果提取的子视图变得可复用或独立有意义,将其移到自己的文件中。
推荐写法:
var body: some View {
List {
HeaderSection(title: title, subtitle: subtitle)
FilterSection(
filterOptions: filterOptions,
selectedFilter: $selectedFilter
)
ResultsSection(items: filteredItems)
FooterSection()
}
}
private struct HeaderSection: View {
let title: String
let subtitle: String
var body: some View {
VStack(alignment: .leading, spacing: 6) {
Text(title).font(.title2)
Text(subtitle).font(.subheadline)
}
}
}
private struct FilterSection: View {
let filterOptions: [FilterOption]
@Binding var selectedFilter: FilterOption?
var body: some View {
ScrollView(.horizontal) {
HStack {
ForEach(filterOptions) { option in
FilterChip(
option: option,
isSelected: selectedFilter == option,
onTap: { selectedFilter = option }
)
}
}
}
}
}
避免写法:
var body: some View {
List {
header
filters
results
footer
}
}
private var header: some View {
VStack(alignment: .leading, spacing: 6) {
Text(title).font(.title2)
Text(subtitle).font(.subheadline)
}
}
private var filters: some View {
ScrollView(.horizontal) {
HStack {
ForEach(filterOptions) { option in
FilterChip(
option: option,
isSelected: selectedFilter == option,
onTap: { selectedFilter = option }
)
}
}
}
}
4) 数据流规则
- 子视图通过
let属性接收数据,通过@Binding接收可变状态,通过闭包接收操作。 - 避免在子视图中直接访问父视图的
@State。 - 使用
@Environment传递跨多层的共享依赖。
5) Observation 使用
- 对于
@Observable类,直接在视图中使用属性访问即可触发更新。 - 不需要
@ObservedObject包装器(除非支持旧版 iOS)。 - 将
@Observable对象通过@Environment注入。
最佳实践
- 每个视图文件保持在 ~150 行以内
- 使用
#Preview宏为每个子视图提供预览 - 命名要表达意图:
UserProfileHeader而非Header - 保持
body简洁,理想情况下一屏可见
兼容工具
Claude CodeCursor
标签
移动端