首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >鸿蒙 架构实战:分层架构、MVVM 与状态驱动 UI

鸿蒙 架构实战:分层架构、MVVM 与状态驱动 UI

原创
作者头像
hunter android
发布2026-08-25 15:02:08
发布2026-08-25 15:02:08
280
举报

一、为什么需要架构模式

随着 HarmonyOS 应用复杂度提升,业务逻辑、UI 交互、数据流散落在各处,代码耦合严重、难以测试、维护成本高。架构模式通过分层解耦、职责分离、单向数据流,让代码更清晰、更可测、更易扩展。

二、分层架构:职责分离的基础

经典三层架构:

  • UI 层:ArkUI 组件、用户交互、状态渲染
  • 业务逻辑层(BLL):数据转换、业务规则、流程编排
  • 数据访问层(DAL):网络请求、数据库操作、本地缓存

2.1 依赖方向与边界

代码语言:javascript
复制
// ❌ 反模式:UI 层直接访问数据库
@Component
struct BadExample {
  async loadData() {
    const db = relationalStore.getRdbStore(...) // UI 层不应知道数据库细节
  }
}

// ✅ 正确:通过 Repository 隔离
export class UserRepository {
  private db: relationalStore.RdbStore
  async getUsers(): Promise<User[]> {
    const result = await this.db.query('users')
    return result.rows.map(row => new User(row))
  }
}

@Component
struct GoodExample {
  private repo = new UserRepository()
  @State users: User[] = []
  
  async aboutToAppear() {
    this.users = await this.repo.getUsers() // UI 只关心业务对象
  }
}

2.2 分层收益

  • 每层可独立测试(Mock Repository 即可测试 UI 逻辑)
  • 数据源切换对上层透明(数据库换 HTTP 接口,UI 无感知)
  • 业务规则集中管理,避免散落在组件中

三、MVVM:状态与视图的双向绑定

MVVM(Model-View-ViewModel)将 UI 状态抽离到 ViewModel,通过数据绑定驱动视图更新。

3.1 结构拆解

  • Model:纯数据对象(如 UserProduct
  • View:ArkUI 组件,只负责渲染和事件分发
  • ViewModel:持有状态、处理交互逻辑、调用 Repository

3.2 完整示例

代码语言:javascript
复制
// Model
export class Todo {
  constructor(public id: string, public text: string, public done: boolean) {}
}

// ViewModel
export class TodoViewModel {
  @State todos: Todo[] = []
  @State inputText: string = ''
  
  private repo = new TodoRepository()
  
  async loadTodos() {
    this.todos = await this.repo.getAll()
  }
  
  async addTodo() {
    if (!this.inputText.trim()) return
    const newTodo = await this.repo.create(this.inputText)
    this.todos = [...this.todos, newTodo]
    this.inputText = ''
  }
  
  async toggleTodo(id: string) {
    const todo = this.todos.find(t => t.id === id)
    if (todo) {
      todo.done = !todo.done
      await this.repo.update(todo)
      this.todos = [...this.todos] // 触发刷新
    }
  }
}

// View
@Component
struct TodoListView {
  @State vm: TodoViewModel = new TodoViewModel()
  
  aboutToAppear() {
    this.vm.loadTodos()
  }
  
  build() {
    Column() {
      TextInput({ text: this.vm.inputText })
        .onChange(v => this.vm.inputText = v)
      Button('添加').onClick(() => this.vm.addTodo())
      
      List() {
        ForEach(this.vm.todos, (todo: Todo) => {
          ListItem() {
            Row() {
              Checkbox().select(todo.done)
                .onChange(() => this.vm.toggleTodo(todo.id))
              Text(todo.text).decoration(todo.done ? TextDecorationType.LineThrough : TextDecorationType.None)
            }
          }
        }, (todo: Todo) => todo.id)
      }
    }
  }
}

3.3 MVVM 的优势与陷阱

优势:

  • View 与业务逻辑解耦,易于替换 UI 框架
  • ViewModel 可单元测试,无需启动 UI
  • 状态集中管理,避免分散在各个组件

陷阱:

  • ViewModel 膨胀:一个页面对应一个 ViewModel,复杂页面会有几百行状态管理代码
  • 双向绑定滥用:@Link 过多导致数据流向不清晰
  • 缺乏全局状态管理:跨页面共享状态需要额外设计(如 Singleton、EventBus)

四、状态驱动 UI:单向数据流

受 Redux/Vuex 启发,状态驱动 UI 强调单向数据流Action → Reducer → State → View

4.1 核心概念

  • State:应用唯一的数据源(Single Source of Truth)
  • Action:描述"发生了什么"的纯对象(如 { type: 'ADD_TODO', text: '...' }
  • Reducer:纯函数,接收旧 State 和 Action,返回新 State
  • View:根据 State 渲染 UI,用户交互触发 Action

4.2 简化实现

代码语言:javascript
复制
// Action 定义
type Action = 
  | { type: 'ADD_TODO', text: string }
  | { type: 'TOGGLE_TODO', id: string }
  | { type: 'SET_TODOS', todos: Todo[] }

// State 定义
interface AppState {
  todos: Todo[]
}

// Reducer
function todoReducer(state: AppState, action: Action): AppState {
  switch (action.type) {
    case 'ADD_TODO':
      return { ...state, todos: [...state.todos, new Todo(Date.now().toString(), action.text, false)] }
    case 'TOGGLE_TODO':
      return { 
        ...state, 
        todos: state.todos.map(t => t.id === action.id ? { ...t, done: !t.done } : t) 
      }
    case 'SET_TODOS':
      return { ...state, todos: action.todos }
    default:
      return state
  }
}

// Store
class Store {
  @State private state: AppState = { todos: [] }
  
  dispatch(action: Action) {
    this.state = todoReducer(this.state, action)
  }
  
  getState(): AppState {
    return this.state
  }
}

// View
@Component
struct TodoApp {
  private store = new Store()
  
  build() {
    Column() {
      Button('添加').onClick(() => this.store.dispatch({ type: 'ADD_TODO', text: '新任务' }))
      List() {
        ForEach(this.store.getState().todos, (todo: Todo) => {
          ListItem() {
            Text(todo.text).onClick(() => this.store.dispatch({ type: 'TOGGLE_TODO', id: todo.id }))
          }
        })
      }
    }
  }
}

4.3 状态驱动的优势

  • 数据流向清晰:Action → State → View,单向流动
  • 时间旅行调试:记录所有 Action,可回溯任意状态
  • 易于测试:Reducer 是纯函数,输入输出确定

五、架构模式选型矩阵

场景

推荐架构

原因

简单工具类应用

分层架构

无需复杂状态管理,三层足够

表单密集、交互复杂

MVVM

ViewModel 集中管理表单状态

多页面共享状态

状态驱动 UI

全局 Store 避免状态分散

实时协作、撤销重做

状态驱动 UI

Action 可序列化、可回放

六、实战建议

  1. 从分层开始:先建立 Repository → Service → ViewModel 的基础结构
  2. 状态提升:当多个组件共享状态时,提升到父组件或全局 Store
  3. 避免过度设计:小项目直接用 @State 即可,不要为了架构而架构
  4. 测试驱动:关键业务逻辑写在 ViewModel/Reducer,保证可测试性
  5. 渐进式重构:遗留代码不要一次性重写,按模块逐步迁移

七、总结

架构模式不是银弹,而是工程实践的沉淀。分层架构提供职责分离的基础,MVVM 适合表单和交互密集的场景,状态驱动 UI 在复杂应用中提供清晰的数据流。选择架构模式时,优先考虑团队认知成本和项目复杂度,避免过度设计。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、为什么需要架构模式
  • 二、分层架构:职责分离的基础
    • 2.1 依赖方向与边界
    • 2.2 分层收益
  • 三、MVVM:状态与视图的双向绑定
    • 3.1 结构拆解
    • 3.2 完整示例
    • 3.3 MVVM 的优势与陷阱
  • 四、状态驱动 UI:单向数据流
    • 4.1 核心概念
    • 4.2 简化实现
    • 4.3 状态驱动的优势
  • 五、架构模式选型矩阵
  • 六、实战建议
  • 七、总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档