Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 第5章:开发 ViewModel 层

第5章:开发 ViewModel 层

ViewModel 的职责与设计

核心职责

ViewModel 在 MVVM 架构中扮演着 业务逻辑处理器 和 数据转换器 的双重角色,其核心职责包括:

  1. 数据准备:从 Model 层获取原始数据并转换为 View 层可直接使用的格式。
  2. 状态管理:通过 @Published 属性包装器维护 UI 状态,驱动视图更新。
  3. 用户交互处理:接收来自 View 层的用户输入事件(如按钮点击),触发业务逻辑。
  4. 依赖协调:管理网络请求、本地存储等外部服务的调用。

设计原则

1. 单一职责原则

// 反例:混杂多个业务域的 ViewModel
class UserDashboardViewModel {
    func loadUserProfile() { ... }
    func fetchLatestOrders() { ... } // ❌ 订单逻辑不应存在于用户面板VM
}

// 正例:拆分职责
class UserProfileViewModel { ... }
class OrderListViewModel { ... }

2. 无视图引用

ViewModel 不应持有任何 SwiftUI View 的引用,仅通过数据绑定与 View 通信:

// 反例
class TodoViewModel {
    var rootView: ContentView? // ❌ 禁止持有视图引用
}

3. 可测试性设计

  • 通过协议抽象外部依赖
  • 使用依赖注入初始化服务
protocol DataServiceProtocol {
    func fetchTodos() async throws -> [Todo]
}

class TodoViewModel {
    private let service: DataServiceProtocol
    
    init(service: DataServiceProtocol) { ... } // ✅ 可注入Mock测试
}

典型结构示例

class ArticleViewModel: ObservableObject {
    // MARK: - Published Properties
    @Published var articles: [Article] = []
    @Published var isLoading = false
    @Published var errorMessage: String?
    
    // MARK: - Dependencies
    private let apiClient: APIClientProtocol
    
    // MARK: - Initialization
    init(apiClient: APIClientProtocol = APIClient()) {
        self.apiClient = apiClient
    }
    
    // MARK: - Public Methods
    @MainActor
    func loadArticles() async {
        isLoading = true
        do {
            articles = try await apiClient.fetchArticles()
            errorMessage = nil
        } catch {
            errorMessage = "加载失败: \(error.localizedDescription)"
        }
        isLoading = false
    }
    
    func deleteArticle(_ id: UUID) { ... }
}

生命周期管理

  • 使用 ObservableObject 协议实现自动视图更新
  • 对于复杂场景可采用 StateObject 管理生命周期:
struct ArticleListView: View {
    @StateObject var viewModel = ArticleViewModel() // ✅ 正确生命周期管理
    
    var body: some View { ... }
}

常见陷阱

  1. 过度臃肿:当单个 ViewModel 超过 400 行代码时,应考虑拆分
  2. 直接修改 View 状态:所有状态变更应通过 ViewModel 的 published 属性
  3. 忽略线程安全:确保 @Published 更新发生在主线程(使用 @MainActor)

最佳实践提示:为每个独立屏幕或功能模块创建专属 ViewModel,避免创建"上帝对象"式的全局 ViewModel。

Last Updated:: 4/25/25, 8:11 PM