MVVM作为主流的前端架构模式,在实际项目开发中是常用的框架,它实现UI与数据的优雅解耦,让代码变得清晰。本文分享鸿蒙MVVM框架的实战经验,基于以下开发环境:开发工具:DevEco Studio 6.0 ReleaseAPI 版本:20
一、 MVVM 框架的简要理解
MVVM(Model、ViewModel、Model) 的核心是数据绑定,而数据绑定的底层实现,笔者认为几乎无一例外都可理解为基于观察者模式。观察目标(Observable):当Model层的数据发生变化时,它会发出变化通知观察者(Observer):视图(View)会监听所需关注的对象数据变化通知,一旦数据发生变化,视图就会重新刷新渲染对应数据的显示二、鸿蒙 ArkTS MVVM实现
1.Model 层
本文仍然用登录界面来介绍,新建用户数据类:src/main/ets/model/UserModel.ets@Observed export class UserModel { username: string = ''; password: string = ''; token: string = ''; isLoggedIn: boolean = false; constructor(username: string = '', password: string = '') { this.username = username; this.password = password; } }
2.ViewModel 层
业务处理层,处理用户登录的业务逻辑:src/main/ets/viewmodel/LoginViewModel.etsimport { UserModel } from '../model/UserModel'; import userNapi, { LoginResult } from 'libuser_napi.so'; @Observed export class LoginViewModel { // 状态数据(驱动UI刷新) user: UserModel = new UserModel(); isLoading: boolean = false; errorMessage: string = ''; loginSuccess: boolean = false; // 更新用户名 setUsername(value: string): void { this.user.username = value; this.errorMessage = ''; } // 更新密码 setPassword(value: string): void { this.user.password = value; this.errorMessage = ''; } // 登录验证 async login(): Promise<boolean> { // 本地校验 if (!this.user.username.trim()) { this.errorMessage = '请输入用户名'; return false; } if (!this.user.password.trim()) { this.errorMessage = '请输入密码'; return false; } if (this.user.password.length < 6) { this.errorMessage = '密码至少6位'; return false; } // 调用NAPI进行服务端验证(模拟) this.isLoading = true; this.errorMessage = ''; try { // 调用C++层NAPI接口 const result: LoginResult = userNapi.login(this.user.username, this.user.password); if (result.success) { this.user.token = result.token; this.user.isLoggedIn = true; this.loginSuccess = true; return true; } else { this.errorMessage = result.message || '登录失败'; return false; } } catch (error) { this.errorMessage = '网络错误,请重试'; return false; } finally { this.isLoading = false; } } // 退出登录 logout(): void { this.user = new UserModel(); this.loginSuccess = false; this.errorMessage = ''; // 调用C++层注销 //userNapi.logout(); } }
3. View 层
简单的登录界面: src/main/ets/view/LoginPage.etsimport { LoginViewModel } from '../viewmodel/LoginViewModel'; @Entry @Component struct LoginPage { @State viewModel: LoginViewModel = new LoginViewModel(); build() { Column({ space: 20 }) { // 标题 Text('用户登录') .fontSize(28) .fontWeight(FontWeight.Bold) .margin({ top: 100, bottom: 50 }) // 用户名输入 Column({ space: 8 }) { Text('用户名') .fontSize(14) .fontColor('#666') .alignSelf(ItemAlign.Start) TextInput({ placeholder: '请输入用户名' , text: this.viewModel.user.username // ← 关键!绑定 VM 值 }) .width('100%') .height(48) .backgroundColor('#F5F5F5') .borderRadius(8) .padding({ left: 16 }) .onChange((value: string) => { this.viewModel.setUsername(value); }) } .width('80%') // 密码输入 Column({ space: 8 }) { Text('密码') .fontSize(14) .fontColor('#666') .alignSelf(ItemAlign.Start) TextInput({ placeholder: '请输入密码', text: this.viewModel.user.password // ← 关键!绑定 VM 值 }) .width('100%') .height(48) .backgroundColor('#F5F5F5') .borderRadius(8) .padding({ left: 16 }) .type(InputType.Password) // 密码输入模式 .onChange((value: string) => { this.viewModel.setPassword(value); }) } .width('80%') // 错误提示 if (this.viewModel.errorMessage) { Text(this.viewModel.errorMessage) .fontSize(14) .fontColor('#FF4444') .margin({ top: 8 }) } // 登录按钮 Button(this.viewModel.isLoading ? '登录中...' : '登 录') .width('80%') .height(50) .backgroundColor(this.viewModel.isLoading ? '#CCCCCC' : '#007DFF') .fontSize(16) .fontColor(Color.White) .borderRadius(8) .margin({ top: 30 }) .enabled(!this.viewModel.isLoading) .onClick(async () => { const success = await this.viewModel.login(); if (success) { // 登录成功,跳转到首页 console.info('登录成功,token:', this.viewModel.user.token); } }) // 登录成功提示 if (this.viewModel.loginSuccess) { Column({ space: 10 }) { Text('✓ 登录成功') .fontSize(18) .fontColor('#52C41A') Text(`欢迎,${this.viewModel.user.username}`) .fontSize(14) .fontColor('#666') Button('退出登录') .width(120) .height(40) .backgroundColor('#FF4444') .fontSize(14) .onClick(() => { this.viewModel.logout(); }) } .margin({ top: 30 }) .padding(20) .backgroundColor('#F6FFED') .borderRadius(8) } } .width('100%') .height('100%') .backgroundColor(Color.White) } }
三、 效果与总结
在执行logout()函数的时候,自动清空输入框中的用户和密码信息:// 退出登录 logout(): void { this.user = new UserModel(); this.loginSuccess = false; this.errorMessage = ''; // 调用C++层注销 //userNapi.logout(); }
总结:MVVM的数据绑定主要是通过以下的方式实现:使用@State装饰器,自动监听LoginViewModel对象的数据变化@State viewModel: LoginViewModel = new LoginViewModel();
绑定控件与LoginViewModel的数据,当LoginViewModel对象中的数据发生变化时候,自动触发UI控件的重绘渲染text: this.viewModel.user.username
控件的值发生变化时,修改LoginViewModel中的值:onChange((value: string) => { this.viewModel.setUsername(value); })
本文主要体现ArkTS的源码,native c++部分未展出,如有需要全工程者,请点赞加关注,留言。谢谢!