MV 模式参考关于判断某个 SwiftUI 功能应保持纯 MV 还是引入视图模型的精炼指导。灵感来源于用户提供的资料《SwiftUI in 2025: Forget MVVM》Thomas Ricouard但在此重写为实用的重构参考。默认立场默认使用 MV视图是轻量的状态表达式和编排点。在转向视图模型之前优先使用State、Environment、Query、.task、.task(id:)和onChange。将业务逻辑保留在服务、模型或领域类型中而不是视图 body 中。在发明视图模型层之前先将大型界面拆分为更小的视图类型。避免手动获取数据或状态管道以免重复 SwiftUI 或 SwiftData 的机制。优先测试服务、模型和转换逻辑视图应保持简单和声明式。何时避免视图模型当视图模型主要是为了以下目的时不要引入它镜像局部视图状态包装已可通过Environment获得的值重复Query、State或Binding驱动的数据流仅仅因为视图 body 太长而存在承载一次性异步加载逻辑而这些逻辑本可以放在.task加局部视图状态中。在这些情况下请简化视图和数据流而不是增加间接层。何时可以合理使用视图模型当以下条件至少满足其一视图模型才是合理的用户明确要求使用视图模型代码库已针对该功能标准化了视图模型模式界面需要一个长期存在的引用模型其行为无法自然地完全放入服务中该功能正在适配一个非 SwiftUI 的 API需要一个专门的桥接对象多个视图共享相同的、特定于展示层的状态而该状态不适合建模为应用级环境数据。即便如此也要尽量让视图模型小巧、明确且非可选。推荐模式局部状态加环境structFeedView:View{Environment(BlueSkyClient.self)privatevarclientenumViewState{caseloadingcaseerror(String)caseloaded([Post])}StateprivatevarviewState:ViewState.loadingvarbody:someView{List{switchviewState{case.loading:ProgressView(Loading feed...)case.error(letmessage):ErrorStateView(message:message,retryAction:{awaitloadFeed()})case.loaded(letposts):ForEach(posts){postinPostRowView(post:post)}}}.task{awaitloadFeed()}}privatefuncloadFeed()async{do{letpoststryawaitclient.getFeed()viewState.loaded(posts)}catch{viewState.error(error.localizedDescription)}}}为什么推荐这样做状态保持在渲染它的 UI 附近依赖来自环境而不是包装对象视图协调 UI 流程而服务负责真正的工作。推荐模式将修饰符用作轻量编排.task(id:searchText){guard!searchText.isEmptyelse{results[]return}awaitsearchFeed(query:searchText)}.onChange(of:isInSearch,initial:false){guard!isInSearchelse{return}Task{awaitfetchSuggestedFeed()}}使用视图生命周期修饰符进行简单、局部的编排。默认不要将这些转换为视图模型除非行为明显超出了视图的承载能力。SwiftData 说明SwiftData 是尽可能将数据流保留在视图内的有力论据。推荐structBookListView:View{Queryprivatevarbooks:[Book]Environment(\.modelContext)privatevarmodelContextvarbody:someView{List{ForEach(books){bookinBookRowView(book:book).swipeActions{Button(Delete,role:.destructive){modelContext.delete(book)}}}}}}避免添加手动获取并镜像相同状态的视图模型除非该功能有明确的理由这样做。测试指导优先测试服务和业务规则模型和状态转换服务层的异步工作流通过预览或更高级别的 UI 测试验证 UI 行为。不要仅仅为了让简单的 SwiftUI 视图可测试而引入视图模型。那通常只会增加仪式感而无助于改善架构。重构检查清单向 MV 重构时移除仅包装环境依赖或局部视图状态的视图模型。当纯视图状态足够时替换可选的或延迟初始化的视图模型。将业务逻辑从视图 body 中抽出放入服务/模型中。保持视图作为 UI 状态、导航和用户操作的薄协调器。在添加新的间接层之前将大型 body 拆分为更小的视图类型。结论将视图模型视为例外而不是默认。在现代 SwiftUI 中默认技术栈是局部状态使用State共享依赖使用EnvironmentSwiftData 支持的集合使用Query轻量编排使用生命周期修饰符业务逻辑使用服务和模型。只有当功能确实需要时才使用视图模型。