鸿蒙 Next MVVM 模式使用
- 2025-06-23 北京
本文字数:3251 字
阅读完需:约 11 分钟
MVVM 模式介绍
在应用开发中,UI 的更新需要随着数据状态的变化进行实时同步,而这种同步往往决定了应用程序的性能和用户体验。为了解决数据与 UI 同步的复杂性,ArkUI 采用了 Model-View-ViewModel(MVVM)架构模式。MVVM 将应用分为 Model、View 和 ViewModel 三个核心部分,实现数据、视图与逻辑的分离。通过这种模式,UI 可以随着状态的变化自动更新,无需手动处理,从而更加高效地管理数据和视图的绑定与更新。
Model:负责存储和管理应用的数据以及业务逻辑,不直接与用户界面交互。
View:负责用户界面展示数据并与用户交互,不包含任何业务逻辑。它通过绑定 ViewModel 层提供的数据来动态更新 UI。
ViewModel:负责管理 UI 状态和交互逻辑。作为连接 Model 和 View 的桥梁,ViewModel 监控 Model 数据的变化,通知 View 更新 UI,同时处理用户交互事件并转换为数据操作。
实现目标:点击列表的点赞,刷新当前 item 的视图,增加点赞信息
先看一个简单例子:
贴一下 ViewModel 的代码:
@ObservedV2export default class StudentViewModel{ @Trace name:string=''}import StudentViewModel from "./StudentViewModel"
@ObservedV2export default class ClassViewModel{ @Trace className:string='' @Trace stus:StudentViewModel[]=[]
addStu(stu:StudentViewModel){ this.stus.push(stu) }}import { SchoolModel } from "../model/SchoolModel";import ClassViewModel from "./ClassViewModel";import StudentViewModel from "./StudentViewModel";import { UIUtils } from "@ohos.arkui.StateManagement";
@ObservedV2export default class SchoolViewModel{ @Trace cls:ClassViewModel[]=[] @Trace name:string=''
// 不推荐 实际开发中不可能直接创建观察者数据,可做测试使用 async load(){ let stu1 = new StudentViewModel() stu1.name='stu1' let stu2 = new StudentViewModel() stu2.name='stu2' let stu3 = new StudentViewModel() stu3.name='stu3' let stu4 = new StudentViewModel() stu4.name='stu4'
let cls1= new ClassViewModel() cls1.className='cls1' cls1.stus.push(stu1) cls1.stus.push(stu2) let cls2= new ClassViewModel() cls2.className='cls2' cls2.stus.push(stu3) cls2.stus.push(stu4)
let sch = new SchoolViewModel() sch.name='sch' sch.cls.push(cls1) sch.cls.push(cls2)
this.cls=[...sch.cls] this.name=sch.name
} async load2(){ let school = new SchoolModel()//获取到的是非可观察的数据结构,例如从网络获取,本地缓存,数据库等数据 await school.load() //将非观察数据变为可观察数据 this.cls = UIUtils.makeObserved< ClassViewModel[]>(school.cls) as ClassViewModel[] this.name = school.name } //增加这个方法,调用的StudentViewModel 的 add方法,同样在StudentModel也需要增加同样的方法,否则上面的UIUtils.makeObserved会报错 addStu(stu:StudentViewModel,index:number){ this.cls[index].addStu(stu) }}
// Argument of type 'ClassModel[]' is not assignable to parameter of type 'ClassViewModel[]'.// Property 'addStu' is missing in type 'ClassModel' but required in type 'ClassViewModel'.
export class ClassModel{ className:string='' stus:StudentModel[]=[] addStu(stu:StudentModel){ this.stus.push(stu) }}
其他类比较简单,就是没有装饰器的基本类,然后看一下 View 层的实现,使用 V2 装饰器,监听数据变化,自动刷新 view
@Entry@ComponentV2struct mvvm{ @Local school:SchoolViewModel = new SchoolViewModel() async aboutToAppear(){ await this.school.load2() } build() { Column(){ Text('学校:'+this.school.name) cls({sch:this.school}) }.height('100%').justifyContent(FlexAlign.Center) }}@ComponentV2 struct cls{ @Param @Require sch:SchoolViewModel @Event add:(stu:StudentViewModel)=>void = (stu:StudentViewModel)=>{} build() { Column(){ List(){ ForEach(this.sch.cls,(itm:ClassViewModel,index:number)=>{ ListItem(){ Column(){ Text('班级:'+itm.className).onClick(()=>{ itm.className='修改班级名字' }) stu({cls:itm}) Button('添加一名学生').onClick(()=>{ let stu = new StudentViewModel() stu.name='新学生' this.sch.addStu(stu,index) }) } } }) } } }}@ComponentV2export struct stu{ @Param @Require cls:ClassViewModel build() { Column(){ Text('学生:') List(){ ForEach(this.cls.stus,(itm:StudentViewModel)=>{ ListItem(){ Row(){ Text(itm.name) Button('添加一名学生').onClick(()=>{ let stu = new StudentViewModel() stu.name='新学生' this.cls.addStu(stu) }) }.width('100%').justifyContent(FlexAlign.Center) } }) } Text('一共'+this.cls.stus.length+'名') Button('清空').onClick(()=>{ this.cls.stus=[] }) } }}
动态列表点赞主要代码:
model 层的数据获取:
export default class BlogList{ datas:BlogData[] = [] //返回非观察数据 async loadData(context:common.UIAbilityContext){ let getJson = await context.resourceManager.getRawFileContent('动态列表数据.json'); let textDecoderOptions: util.TextDecoderOptions = { ignoreBOM : true }; let textDecoder = util.TextDecoder.create('utf-8',textDecoderOptions); let result = textDecoder.decodeToString(getJson); let resultJson : ResponseResult= JSON.parse(result) let ares :Array<blogdata>= resultJson.data as Array<blogdata> ares.forEach((tab:BlogData,index:number)=>{ this.datas.push(tab) }) return this.datas }}</blogdata></blogdata>viewModel 层数据处理:
@ObservedV2export default class BlogListViewModel{ @Trace datas:BlogDataViewModel[] = []
async loadData(context:common.UIAbilityContext){ //获取model层的数据 let blogList = new BlogList() await blogList.loadData(context)//将非观察数据变为可观察数据 this.datas = UIUtils.makeObserved<array<blogdataviewmodel>>(blogList.datas) as Array<blogdataviewmodel> }//增加点赞数据 add(pra:PraiseViewModel,index:number){ this.datas[index].praiseList=[... this.datas[index].praiseList,pra] }}</blogdataviewmodel></array<blogdataviewmodel>view 层主要代码:
list列表展示: List({ space: 10 }) { ForEach(this.blogModel.datas,(item:BlogDataViewModel,index:number)=>{ ListItem(){ ItemBlog({ pageInfos: this.pageInfos,blogdata:item,changePraise:(pra:PraiseViewModel)=>{ this.blogModel.add(pra,index) } }) } }) }item 点击:Text('点赞'+this.blogdata.praiseList.length).fontColor(Color.White).fontSize(16).onClick(()=>{ let praise =new PraiseViewModel() praise.userId+='1'+Math.random() praise.userName='11212' //@Event的回调事件 this.changePraise(praise)})
版权声明: 本文为 InfoQ 作者【auhgnixgnahz】的原创文章。
原文链接:【http://xie.infoq.cn/article/c31390b9f380d30202ff06dfd】。文章转载请联系作者。
auhgnixgnahz
还未添加个人签名 2018-07-10 加入
还未添加个人简介









评论