鸿蒙Next MVVM模式使用

2025-06-27 22:44:22
106次阅读
0个评论

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的视图,增加点赞信息 最终实现.gif

先看一个简单例子: 简单实现.gif 贴一下ViewModel的代码:

@ObservedV2
export default class StudentViewModel{
  @Trace name:string=''
}
import StudentViewModel from "./StudentViewModel"

@ObservedV2
export  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";

@ObservedV2
export  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
@ComponentV2
struct 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)
              })
            }
          }
        })
      }
    }
  }
}
@ComponentV2
export 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
  }
}

viewModel层数据处理:

@ObservedV2
export 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]
 }
}

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)
})
收藏00

登录 后评论。没有帐号? 注册 一个。