及时做APP开发实战(一)-MVVM架构设计实践
本文将详细介绍如何在HarmonyOS NEXT应用中实现MVVM架构,以"及时做"番茄待办应用为例,展示分层设计的最佳实践。
一、为什么选择MVVM架构?
在移动应用开发中,良好的架构设计是项目可维护性和扩展性的基石。MVVM(Model-View-ViewModel)模式通过清晰的职责分离,让代码更易于理解和维护。
1.1 传统开发模式的痛点
很多初学者习惯将所有代码写在页面组件中,导致:
- 代码臃肿:一个文件动辄上千行
- 难以测试:业务逻辑与UI耦合
- 复用困难:相同逻辑重复编写
- 维护成本高:修改一处影响多处
1.2 MVVM的优势
MVVM模式通过三层分离解决了上述问题:
┌─────────────────────────────────────┐ │ View(视图层) │ │ 负责UI展示和用户交互 │ └──────────────┬──────────────────────┘ │ 数据绑定 ┌──────────────▼──────────────────────┐ │ ViewModel(业务逻辑层) │ │ 负责业务处理和状态管理 │ └──────────────┬──────────────────────┘ │ 数据操作 ┌──────────────▼──────────────────────┐ │ Model(数据模型层) │ │ 负责数据定义和存储 │ └─────────────────────────────────────┘【图1:MVVM架构层次示意图】
二、项目结构设计
在"及时做"应用中,我们采用以下目录结构:
entry/src/main/ets/ ├── model/ # 数据模型层 │ ├── TodoItem.ets # 待办任务模型 │ └── PomodoroRecord.ets # 番茄钟记录模型 ├── viewmodel/ # 业务逻辑层 │ ├── TodoViewModel.ets # 待办业务管理 │ └── PomodoroViewModel.ets # 番茄钟业务管理 ├── utils/ # 工具类层 │ ├── PreferencesHelper.ets # 持久化服务 │ └── CommonUtil.ets # 通用工具 └── pages/ # UI页面层 └── Index.ets # 主页面这种结构清晰明了,每层职责分明:
- model:定义数据结构,不包含业务逻辑
- viewmodel:处理业务逻辑,协调model和view
- utils:提供通用功能,如存储、格式化等
- pages:负责UI渲染和用户交互
【图2:项目目录结构截图】
三、Model层设计
Model层是数据的基础,我们使用ArkTS的interface定义数据结构。
3.1 待办任务模型
/** * 待办任务数据模型 */exportinterfaceTodoItem{/** 唯一标识,使用时间戳生成 */id:number;/** 任务文本内容 */text:string;/** 完成状态 */completed:boolean;/** 创建时间戳 */createdAt:number;/** 番茄钟完成次数 */pomodoroCount:number;}/** * 创建新的待办任务 */exportfunctioncreateTodoItem(text:string):TodoItem{return{id:Date.now(),text:text,completed:false,createdAt:Date.now(),pomodoroCount:0};}设计要点:
- 使用interface而非class:ArkTS推荐使用interface定义纯数据结构
- 语义化命名:每个字段都有清晰的注释说明
- 工厂函数:提供createTodoItem简化对象创建
- 时间戳ID:使用Date.now()生成唯一标识
3.2 番茄钟记录模型
/** * 番茄钟记录数据模型 */exportinterfacePomodoroRecord{/** 唯一标识 */id:number;/** 关联的待办任务ID */todoId:number;/** 单次专注时长(分钟) */duration:number;/** 完成时间戳 */completedAt:number;}/** * 创建新的番茄钟记录 */exportfunctioncreatePomodoroRecord(todoId:number,duration:number):PomodoroRecord{return{id:Date.now(),todoId:todoId,duration:duration,completedAt:Date.now()};}注意todoId字段的设计,它建立了番茄钟记录与待办任务的关联关系,这是关系型数据建模的关键。
四、ViewModel层实现
ViewModel是MVVM的核心,它承上启下,既管理数据又为View提供服务。
4.1 待办业务管理
import{TodoItem,createTodoItem}from'../model/TodoItem';import{PreferencesHelper}from'../utils/PreferencesHelper';/** * 待办任务业务逻辑管理类 */exportclassTodoViewModel{privatepreferencesHelper:PreferencesHelper|null=null;privatetodoList:TodoItem[]=[];/** * 初始化ViewModel */asyncinit():Promise<void>{this.preferencesHelper=awaitPreferencesHelper.getInstance();awaitthis.loadTodoList();}/** * 加载待办列表 */privateasyncloadTodoList():Promise<void>{if(this.preferencesHelper){this.todoList=awaitthis.preferencesHelper.getTodoList();}}/** * 添加新待办 */asyncaddTodo(text:string):Promise<TodoItem>{consttodo=createTodoItem(text);this.todoList.push(todo);if(this.preferencesHelper){awaitthis.preferencesHelper.addTodo(todo);}returntodo;}/** * 切换待办完成状态 */asynctoggleTodoComplete(todoId:number):Promise<void>{consttodo=this.todoList.find(item=>item.id===todoId);if(todo){todo.completed=!todo.completed;awaitthis.updateTodo(todo);}}/** * 获取未完成的待办数量 */getActiveTodoCount():number{returnthis.todoList.filter(item=>!item.completed).length;}}【图3:主页图】
4.2 设计要点解析
- 异步初始化:init方法使用async/await处理异步加载
- 内存缓存:todoList保持内存中的数据,减少IO操作
- 单一职责:每个方法只做一件事
- 错误处理:通过null检查避免空指针异常
4.3 番茄钟业务管理
exportclassPomodoroViewModel{privatepreferencesHelper:PreferencesHelper|null=null;privatetodoViewModel:TodoViewModel|null=null;privaterecords:PomodoroRecord[]=[];// 番茄钟配置privatestaticreadonlyDEFAULT_DURATION=25;privatestaticreadonlySHORT_BREAK=5;privatestaticreadonlyLONG_BREAK=15;/** * 完成一次番茄钟 */asynccompletePomodoro(todoId:number,duration:number=PomodoroViewModel.DEFAULT_DURATION):Promise<PomodoroRecord>{constrecord=createPomodoroRecord(todoId,duration);this.records.push(record);if(this.preferencesHelper){awaitthis.preferencesHelper.addPomodoroRecord(record);}// 更新待办的番茄钟完成次数if(this.todoViewModel){awaitthis.todoViewModel.incrementPomodoroCount(todoId);}returnrecord;}/** * 获取今日专注总时长 */getTodayTotalDuration():number{consttoday=newDate();today.setHours(0,0,0,0);consttodayTimestamp=today.getTime();returnthis.records.filter(record=>record.completedAt>=todayTimestamp).reduce((total,record)=>total+record.duration,0);}}这里体现了ViewModel之间的协作:PomodoroViewModel需要通知TodoViewModel更新番茄钟计数。
五、View层使用示例
在页面组件中,我们通过ViewModel获取和操作数据:
import{TodoViewModel}from'../viewmodel/TodoViewModel';@Entry@Componentstruct Index{@StatetodoList:TodoItem[]=[];privatetodoViewModel:TodoViewModel=newTodoViewModel();asyncaboutToAppear(){awaitthis.todoViewModel.init();this.todoList=this.todoViewModel.getTodoList();}build(){Column(){// UI渲染ForEach(this.todoList,(todo:TodoItem)=>{Text(todo.text).onClick(async()=>{awaitthis.todoViewModel.toggleTodoComplete(todo.id);this.todoList=this.todoViewModel.getTodoList();})})}}}关键点:
- 生命周期初始化:在aboutToAppear中初始化ViewModel
- 状态管理:使用@State装饰器实现响应式更新
- 数据绑定:UI直接绑定ViewModel的数据
六、架构优势总结
通过MVVM架构,我们实现了:
- 代码组织清晰:每层职责明确,易于定位代码
- 可测试性强:ViewModel可以独立测试,不依赖UI
- 复用性好:ViewModel可在多个页面复用
- 维护成本低:修改业务逻辑不影响UI结构
在HarmonyOS NEXT开发中,MVVM模式与ArkUI的响应式特性完美契合,是构建高质量应用的理想选择。
七、最佳实践建议
- 保持Model纯粹:只定义数据结构,不包含业务逻辑
- ViewModel单一职责:一个ViewModel管理一类业务
- 合理使用工具类:通用功能抽取到utils层
- 异步处理规范:统一使用async/await处理异步操作
- 错误处理完善:关键操作添加try-catch和null检查
通过这套架构,"及时做"应用具备了良好的可扩展性,为后续功能开发打下了坚实基础。