鸿蒙MVVM模式介绍与使用

文章目录

  • 鸿蒙MVVM模式介绍与使用
    • 背景
    • MVVM模式介绍
    • 相关装饰器介绍
      • @State状态变量
      • @Prop、@Link的作用
    • MVVM架构模式的实现以及相关装饰器的使用
      • 具体实现效果
    • 总结

鸿蒙MVVM模式介绍与使用

背景

最近在学习鸿蒙开发,想到了以前写安卓移动端应用时经常会用到的MVVM架构模式,就想着能不能在鸿蒙开发的过程中也使用一下,在鸿蒙开发文档里找到了https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-mvvm-V5这篇文档,在这篇文档的基础上做了一些深化和总结

MVVM模式介绍

在应用开发中,UI的更新需要随着数据状态的变化进行实时同步,而这种同步往往决定了应用程序的性能和用户体验。为了解决数据与UI同步的复杂性,ArkUI采用了 Model-View-ViewModel(MVVM)架构模式。MVVM 将应用分为Model、View和ViewModel三个核心部分,实现数据、视图与逻辑的分离。通过这种模式,UI可以随着状态的变化自动更新,无需手动处理,从而更加高效地管理数据和视图的绑定与更新。
ArkUI采取MVVM = Model + View + ViewModel模式。

  1. Model层:存储数据和相关逻辑的模型。
    1. 负责整个应用的原始数据和数据库操作
    2. 可以将一些网络请求获取的数据局放在这里
  2. View层:在ArkUI中通常是@Component装饰组件渲染的UI。
    1. 页面组件,某些业务组件,通用组件等.
  3. ViewModel层:在ArkUI中,ViewModel是存储在自定义组件的状态变量、LocalStorage和AppStorage中的数据。
    1. ViewModel层负责处理Model层获取的一些数据,并且进行逻辑处理,以及对View的渲染(向上刷新ui,向下更新数据)
    2. 可以把它理解为后端开发经常会接触到的service层

ArkUI的UI开发开发模式即是MVVM模式,而状态管理在MVVM模式中扮演者ViewModel的角色,向上刷新UI,向下更新数据.

最重要的就是理解向上刷新ui,向下更新数据.

请添加图片描述

同步数据时使用事件驱动,例如在页面加载之前执行的方法的aboutToAppear()方法可以进行数据的初始化,执行一些接口方法之类之类的数据获取方法.
这些方法主要实现位置就在ViewModel中

  1. 不可跨层访问

    • View层不可以直接调用Model层的数据,只能通过ViewModel提供的方法进行调用。
    • Model层数据,不可以直接操作UI,Model层只能通知ViewModel层数据有更新,由ViewModel层更新对应的数据。
  2. 非父子组件间不可直接访问**(这是针对View层设计的核心原则)**

    • 禁止直接访问父组件(使用事件或是订阅能力)
    • 禁止直接访问兄弟组件能力。这是因为组件应该仅能访问自己看的见的子节点(通过传参)和父节点(通过事件或通知),以此完成组件之间的解耦。

    主要是为了减少代码耦合程度

相关装饰器介绍

@State状态变量

@State装饰器作为最常用的装饰器,用来定义状态变量,一般作为父组件的数据源,当开发者点击时,通过触发状态变量的更新从而刷新UI,去掉@State则不再支持刷新UI。

@state装饰器是用来控制当前组件的数据源,使用有@state装饰器装饰的组件时会得实时刷新UI的能力.

@Prop、@Link的作用

随着需要渲染的组件越来越多,@Entry组件必然需要进行拆分,为此拆分出的子组件就需要使用@Prop和@Link装饰器:

  1. @Prop是父子间单向传递,子组件会深拷贝父组件数据,可从父组件更新,也可自己更新数据,但不会同步父组件数据。
  2. @Link是父子间双向传递,父组件改变,会通知所有的@Link,同时@Link的更新也会通知父组件对应变量进行刷新。

说白了就是@Prop是单向传递,子同步父的数据,父不同步子的数据

@Link一个是双向传递,父子数据同步

MVVM架构模式的实现以及相关装饰器的使用

先看看test项目结构

![请添加图片描述](https://i-blog.csdnimg.cn/direct/a04d801f3a214b0481b48370653c4974.png)
  1. 定义基础组件
/*** 标题Title*/
@Preview
@Component
export struct Title {// @Prop装饰的变量,由于值是上层组件给的,所以不需要进行值的初始化@Prop titleStatic: TitleStatic;// 与上层组件建立双向链接,实时刷新数据和ui@Link selectValue: string;// 无参构造组件方法@BuilderParam titleBuilderParam: () => void = this.doNothingBuilder@BuilderdoNothingBuilder() {}build() {// 第一层Row() {// 第二层Row() {Text(this.titleStatic.title).fontColor(this.titleStatic.fontColor).opacity(1).fontSize(16)}.width('30%')// 第二层Row() {if (this.titleStatic.isShowSelect) {Select([{ value: this.titleStatic.selectItems[0].toString() },{ value: this.titleStatic.selectItems[1].toString() },{ value: this.titleStatic.selectItems[2].toString() },{ value: this.titleStatic.selectItems[3].toString() }]).selected(0).value('2024').font({ size: 16, weight: 500 }).fontColor(this.titleStatic.fontColor).selectedOptionFont({ size: 16, weight: 400 }).optionFont({ size: 16, weight: 400 }).arrowPosition(ArrowPosition.END).menuAlign(MenuAlignType.START, { dx: 0, dy: 0 }).backgroundColor(100000000).optionWidth(200).optionHeight(300).divider(null).opacity(1).onSelect((index: number, value?: string | undefined) => {// 逻辑处理this.selectValue = value || '';})}if (this.titleStatic.isShowTo) {Image($r('app.media.white_right_arrow')).width(16)}}.width('70%').justifyContent(FlexAlign.End)}.width('100%').backgroundColor(100000000)}
}
//标题静态数据
export interface TitleStatic {title: ResourceStr; // 标题文字fontColor: ResourceColor; // 文字颜色isShowTo: boolean; // 是否显示箭头isShowSelect: boolean; // 是否显示选择框selectItems: Array<string>; // 可以选择的条目
}
  1. 定义组合组件
@Component
export struct MyComponent1 {// 静态数据.直接传给下层组件titleStatic1: TitleStatic = {title: '测试1',fontColor: '#191919',isShowTo: true,isShowSelect: true,selectItems: ['2024', '2023', '2022', '2021']};titleStatic2: TitleStatic = {title: '测试2',fontColor: '#191919',isShowTo: true,isShowSelect: true,selectItems: ['2024', '2023', '2022', '2021']};// 组件动态选择@Link selectValue1: string;@Link selectValue2: string;// 无参组件构造函数@BuilderParam titleBuilderParam: () => void = this.doNothingBuilder@BuilderdoNothingBuilder() {}build() {// 第一层Column({ space: 10 }) {Title({titleStatic: this.titleStatic1,selectValue: this.selectValue1})Title({titleStatic: this.titleStatic2,selectValue: this.selectValue2})}.width('100%').backgroundColor('rgba(255, 255, 255, 0.72)').padding(10).borderRadius(15)}
}
  1. 定义Model
/*** @ProjectName : MyApplicationTest* @FileName : BaseModel* @Author : 君君超有趣* @Time : 2024/11/21 17:02* @Description : Model基类*/
export class BaseModel {}/*** 数据来源 Model层*/
@Observed
export class TestModel extends BaseModel {/*** @Author 君君超有趣* @Description 获取数据* @Date 10:40 2024/11/18* @Param* @return**/async getIssueData<T>(year:number) {return year;}
}
  1. 定义ViewModel
/*** @ProjectName : MyApplicationTest* @FileName : BaseViewModel* @Author : 君君超有趣* @Time : 2024/11/21 17:04* @Description : 基础ViewModel类,所有ViewModel都应继承此类*/
export abstract class BaseViewModel<T extends BaseModel> {/*** 模型实例* @type T*/public mModel: T | undefined;/*** 构造函数* @param {T} model 模型实例*/constructor(model: T) {// 初始化模型实例this.mModel = model;}/*** 销毁方法,用于释放资源*/public onDestroy() {// 将模型实例设置为undefined,释放资源this.mModel = undefined;}
}
/*** ViewModel层,连接Model层和ViewModel层*/
@Observed
export class TestVM extends BaseViewModel<TestModel> {//提供给View的动态数据date1: string = '';date2: string = '';/*** @Author 君君超有趣* @Description //处理原始数据* @Date 15:25 2024/11/14* @Param* @return**/async getYearData(index: number, year: number): Promise<string> {return new Promise((resolve, reject) => {this.mModel?.getIssueData<requestResult<issue>>(year).then((res) => {// 进行数据处理if (index === 0) {this.date1 = res + '年';}if (index === 1) {this.date2 = res + '年';}// 返回结果值resolve('成功')}).catch((e: Error) => {reject('出现异常')})})}
}
  1. 定义具体页面Entity
@Entry
@Component
struct Index {// 拿到ViewModel对象projectManagerVM: TestVM = new TestVM(new TestModel());// 监听值的变化@State @Watch('onSelectValueChange1') selectValue1: string = '';@State @Watch('onSelectValueChange2') selectValue2: string = '';// 页面本地值,用来同步修改本层组件的值@State date1: string = '0';@State date2: string = '0';/*** @Author 君君超有趣* @Description 监听第一个年份选择变化* @Date 11:22 2024/11/21* @Param* @return**/onSelectValueChange1() {// TODO:业务逻辑console.info('selectValue1值发生变化:' + this.selectValue1)this.projectManagerVM.getYearData(0, Number(this.selectValue1)).then(res => {this.date1 = this.projectManagerVM.date1;console.info('date1值发生变化:' + this.date1)})}/*** @Author 君君超有趣* @Description 监听第二个年份选择变化* @Date 11:23 2024/11/21* @Param* @return**/onSelectValueChange2() {// TODO:业务逻辑console.info('selectValue2值发生变化:' + this.selectValue2)this.projectManagerVM.getYearData(1, Number(this.selectValue2)).then(res => {this.date2 = this.projectManagerVM.date2;console.info('date2值发生变化:' + this.date2)})}build() {Column() {MyComponent1({selectValue1: this.selectValue1,selectValue2: this.selectValue2})Text(this.date1)Text(this.date2)}}
}

具体实现效果

请添加图片描述
请添加图片描述

总结

记住一句话就好了:向上刷新ui,向下更新数据.

以上就是我接触鸿蒙后学习到的在鸿蒙开发下使用MVVM架构的具体方法,有什么疑问或者建议可以联系我.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/62194.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决SpringBoot连接Websocket报:请求路径 404 No static resource websocket.

问题发现 最近在工作中用到了WebSocket进行前后端的消息通信&#xff0c;后端代码编写完后&#xff0c;测试一下是否连接成功&#xff0c;发现报No static resource websocket.&#xff0c;看这个错貌似将接口变成了静态资源来访问了&#xff0c;第一时间觉得是端点没有注册成…

【RISC-V CPU 专栏 -- 香山处理器介绍】

文章目录 RISC-V 香山处理器介绍雁栖湖处理器南湖处理器RISC-V 香山处理器介绍 相信很多小伙伴对于“香山”都不陌生,它是一款开源RISC-V处理器核,香山的每一代架构,都是采用了湖的名字,第一代架构被命名为雁栖湖,第二代架构则叫做 “南湖”。 “雁栖湖”这款处理器的 R…

网络安全原理与技术思考题/简答题

作业1&#xff08;第1章、第2章、第8章&#xff09; 1. 网络安全的基本属性有哪些&#xff1f;简单解释每个基本属性的含义。网络安全的扩展属性包括哪些&#xff1f; 基本属性&#xff1a; 1.机密性(Confidentiality)&#xff1a; 含义&#xff1a;确保信息不被未授权的用户…

小米note pro一代(leo)线刷、twrp、magisk、TODO: android源码编译

本文主要说android5 整体思路 android 5.1 twrp magisk Zygisk(Riru) Dreamland(xposed) Riru不支持android5.1, 因此只能选择Zygisk : 如果你正在使用 Android 5&#xff0c;你必须使用 Zygisk 因为 Riru 并不支持 Android 5. 基于magisk之上的xposed 其中提到的 作者…

英语知识网站:Spring Boot技术构建

6系统测试 6.1概念和意义 测试的定义&#xff1a;程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为&#xff1a; 目的&#xff1a;发现程序的错误&#xff1b; 任务&#xff1a;通过在计算机上执行程序&#xff0c;暴露程序中潜在的错误。 另一个…

Android 常用命令和工具解析之GPU相关

目录 1、GPU基本信息 1.1 获取GPU基本信息 1.2 伪造GPU基本信息 2、GPU内存信息 3、经典案例 案例1&#xff1a;GPU伪造信息方案 案例2&#xff1a;GPU内存统计算法 GPU 指的是 Graphics Processing Unit&#xff0c;即图形处理单元。GPU 是一种专门用于处理图形和图像相…

NIO三大组件

现在互联网环境下&#xff0c;分布式系统大相径庭&#xff0c;而分布式系统的根基在于网络编程&#xff0c;而netty恰恰是java领域的网络编程的王者&#xff0c;如果要致力于并发高性能的服务器程序、高性能的客户端程序&#xff0c;必须掌握netty网络编程。 NIO基础 NIO是从ja…

Vue3 el-table 默认选中 传入的数组

一、效果&#xff1a; 二、官网是VUE2 现更改为Vue3写法 <template><el-table:data"tableData"border striperow-key"id"ref"tableRef":cell-style"{ text-align: center }":header-cell-style"{background: #b7babd…

【Python】绝地求生PUBG等FPS游戏辅助压枪软件脚本编写

【Python】绝地求生PUBG等FPS游戏辅助压枪软件脚本编写 更新以gitee为准&#xff1a; https://gitee.com/Mike_Zhou_Group/PUBG_Key/开源的是辅助压枪软件 不开源的是将之前的地图测距和压枪辅助整合在一起的整合包 关于地图测距&#xff1a; https://gitee.com/Mike_Zhou_G…

深度学习使用LSTM实现时间序列预测

大家好&#xff0c;LSTM是一种特殊的循环神经网络&#xff08;RNN&#xff09;架构&#xff0c;它被设计用来解决传统RNN在处理长序列数据时的梯度消失和梯度爆炸问题&#xff0c;特别是在时间序列预测、自然语言处理和语音识别等领域中表现出色。LSTM的核心在于其独特的门控机…

kafka进阶_2.存储消息

文章目录 一、存储消息介绍二、副本同步2.1、数据一致性2.2、HW在副本之间的传递 如果想了解kafka基础架构和生产者架构可以参考 kafka基础和 Kafka进阶_1.生产消息。 一、存储消息介绍 数据已经由生产者Producer发送给Kafka集群&#xff0c;当Kafka接收到数据后&#xff0c…

HTML飞舞的爱心

目录 系列文章 写在前面 完整代码 代码分析 写在后面 系列文章 序号目录1HTML满屏跳动的爱心&#xff08;可写字&#xff09;2HTML五彩缤纷的爱心3HTML满屏漂浮爱心4HTML情人节快乐5HTML蓝色爱心射线6HTML跳动的爱心&#xff08;简易版&#xff09;7HTML粒子爱心8HTML蓝色…

leetcode 3206. 交替组 I 简单

给你一个整数数组 colors &#xff0c;它表示一个由红色和蓝色瓷砖组成的环&#xff0c;第 i 块瓷砖的颜色为 colors[i] &#xff1a; colors[i] 0 表示第 i 块瓷砖的颜色是 红色 。colors[i] 1 表示第 i 块瓷砖的颜色是 蓝色 。 环中连续 3 块瓷砖的颜色如果是 交替 颜色&…

拥抱极简主义前端开发:NoCss.js 引领无 CSS 编程潮流

在前端开发的世界里&#xff0c;我们总是在不断追寻更高效、更简洁的方式来构建令人惊艳的用户界面。而今天&#xff0c;我要向大家隆重介绍一款具有创新性的工具 ——NoCss.js&#xff0c;它将彻底颠覆你对传统前端开发的认知&#xff0c;引领我们进入一个全新的无 CSS 编程时…

基于QT实现贪吃蛇

0.项目展示 1.游戏大厅界面搭建 1.1 效果展示 1.2 背景添加 通过重写paintEvent事件来绘画界面 部分窗口大小&#xff0c;标题&#xff0c;图标的优化 1.3 开始按钮 使用CSS机制&#xff0c;添加样式&#xff0c;去掉边框 1.4 跳转游戏界面 1.5 问题&#xff1a;如何实现…

Navicat 预览变更sql

需求 用了Flyway&#xff08;数据库迁移工具&#xff09;后&#xff0c;需要记录变更sql&#xff0c;所以要知道变更sql。 查看方式 Navicat提供了预览变更sql功能&#xff0c;右击表---->设计表&#xff0c;比如修改字段后&#xff0c;点击SQL预览标签页&#xff0c; 顺…

数据结构——用数组实现栈和队列

目录 用数组实现栈和队列 一、数组实现栈 1.stack类 2.测试 二、数组实现队列 1.Queue类 2.测试 查询——数组&#xff1a;数组在内存中是连续空间 增删改——链表&#xff1a;链表的增删改处理更方便一些 满足数据先进后出的特点的就是栈&#xff0c;先进先出就是队列…

【8210A-TX2】Ubuntu18.04 + ROS_ Melodic + TM-16多线激光 雷达评测

简介&#xff1a;介绍 TM-16多线激光雷达 在8210A载板&#xff0c;TX2核心模块环境&#xff08;Ubuntu18.04&#xff09;下测试ROS驱动&#xff0c;打开使用RVIZ 查看点云数据&#xff0c;本文的前提条件是你的TX2里已经安装了ROS版本&#xff1a;Melodic。 大家好&#xff0c;…

安装QT6.8(MSVC MinGW)+QT webengine+QT5.15.2

本篇主要针对只使用过QT5的qmake&#xff0c;没有用过MSVC&#xff0c;VS的老同学。 建议一部分一部分安装&#xff0c;全部勾选安装遇到问题会中断&#xff0c;前功尽弃。 我自己需要的是QT5&#xff0c;编出的软件用在公司设备上。 QT6&#xff1a;建议也安装学习&#xf…

【我在CSDN成长】我的五周年创作纪念日

感叹 五年的时光匆匆而过&#xff0c; 像一阵风&#xff0c;拂过岁月的湖面&#xff0c; 泛起层层涟漪&#xff0c;又悄然离去。 曾经的欢笑与泪水&#xff0c; 那些奋斗的日夜&#xff0c; 如同电影般在脑海中放映&#xff0c; 却已成为遥远的回忆。 五年&#xff0c;说长不长…