鸿蒙开发-HarmonyOS UI架构

初步布局Index

当我们新建一个工程之后,首先会进入Index页。我们先简单的做一个文章列表的显示

class Article {
  title?: string
  desc?: string
  link?: string
}

@Entry
@Component
struct Index {
  @State articles: Article[] = []
  
  build() {
    Row() {
      Scroll() {
        Column() {
          ForEach(this.articles, (item: Article) => {
            Column() {
              Text(item.title)
                .fontWeight(FontWeight.Bold)
              Text(item.desc)
              Text("----------")
            }
          }, (item: Article) => {
            return item.link
          })
        }
        .width('100%')
      }
    }
    .height('100%')
  }
}

这样,我们只要把articles里面填充数据,就能正常显示一个列表了。

数据从哪来

可以看到上面的代码里是没有数据的,只有一个空数组。我们想要从网络获取数据。那么,数据怎么来呢?最简单粗暴的写法就是在aboutToAppear()中异步发送get请求,然后更新articles数组。

登录后复制

aboutToAppear() {
  // 请求网络数据
  axios.get(url).then(response => {  
    // 更新this.articles
  }
}

好,现在Index界面依赖了网络库,甚至会依赖三方的axios库。在我之前一个项目中,还依赖过端云的agconnect库。于是Previewer直接报错,说因为有agconnect的依赖,Previewer编译失败。

我们可以看到Index和数据获取的逻辑强耦合在了一起。没有专注于他自身的UI布局的功能。

数据请求扔给另一个类IndexViewModel

那一堆网络请求和处理response的代码,看了就头疼。于是我们初步的设想就是把他完全丢给另一个类去处理,IndexViewModel

@Observed // 这个不能漏,当类成员变化时可以被UI监听到
export default class IndexViewModel {
  articles?: Array<Article>

  refreshData() {
    // 请求网络数据
    // 更新this.articles
  }
}

那么Index里变成了

  @State viewModel: IndexViewModel = new IndexViewModel() 
  aboutToAppear() {
    this.viewModel.refreshData()
  }

现在Index只依赖一个IndexViewModel了。将来无论扩展到多少数据,统一从IndexViewModel里面读取。refreshData()里面也可以填任意多个其他的请求数据源。

可以预览了吗

我们知道,如果只布局一个固定界面,连数据都不需要,那是最简单的,预览也是没问题的。当涉及到数据的依赖,那问题就开始复杂了。Previewer的数据从哪里获得?我们知道即使现在我们把所有网络请求和数据成员都放到了IndexViewModel里面,但这也只是让Index界面没那么多代码,仅此而已。Index界面和IndexViewModel的依赖还是实实在在存在的。也就是说,Index界面还是依赖着真实的数据源,这将使未来Previewer的工作带来更多不确定性。
聪明的你一定想到了,可以写一个IndexViewModelMock类,和IndexViewModel结构一模一样,只是refreshData()里给articles赋值一个假数据。所以我们此时为了代码有条理,提取一个接口,叫IndexViewModelInterface
这样,Index里面的成员就变成了这样

// 真机运行时
@State viewModel: IndexViewModelInterface = new IndexViewModel()
// 使用Previewer时
@State viewModel: IndexViewModelInterface = new IndexViewModelMock()

现在我们又进了一步,可以用假数据预览了。但是还有手动切换数据源的操作。
哦对了,这个解决方案看似很理想,但似乎Arkts对这种结构并不支持。当@State viewModel: IndexViewModelInterface这样声明的成员,调用接口里的方法,会在运行时报错,说无法调用方法。

Previewer和Run的数据源隔离

现在我们做了很多重构,比最初的意大利面有条理很多。但手动切换终究还是不优雅,主要还是麻烦。我们能不能,只让UI布局做UI布局的事情,彻底把数据请求解耦。
声明一个struct IndexContentIndex的布局变成这样

  build() {
    Column() {
      IndexContent({ viewModel: this.viewModel })
    }
  }

显然Index的成员这样声明

viewModel: IndexViewModel = new IndexViewModel()

把之前所有的Index下的布局,放到IndexContent中,然后IndexContent的成员这样声明

@Prop viewModel: IndexViewModel

这样,Index里面包了一个IndexContent,数据的请求由Index控制,IndexContent完全被动接受数据,并进行UI布局。
运行一下,确认App可以正常运行。

那么,我们现在能预览Index了吗?不,我们只需要预览IndexPreviewer就行了。布局的本体现在在IndexPreviewer里的IndexContent里面。
新建一个struct IndexPreviewer,同样,布局里面只包含一个IndexContent

@Preview
@Component
struct IndexPreviewer {
  viewModel: IndexViewModel = new IndexViewModel()

  async aboutToAppear() {
    // 刷新数据
  }

  build() {
    IndexContent({ viewModel: this.viewModel })
  }
}

稍后将重构,给IndexPreviewer里面提供假数据。
这样,由于main_pages.json中定义的页面路径是"pages/Index",所以运行时会显示Index页面中的内容。预览时,不要去预览Index,只需要预览IndexPreviewer,就能快速调整布局。

分离请求和view model

还记得上文提到的ViewModelInterface不管用吗?refreshData()在接口里,运行时调用会报错。于是,我们再把articlesrefreshData()分开,refreshData()放到一个新建的类IndexModel中。
这样,IndexPreviewerIndex里面依赖的成员都是viewModel: IndexViewModel = new IndexViewModel(),而IndexModel可以继承自一个抽象类(之后会解释为什么不是接口)IndexModelBase,再创建一个IndexModelMock继承自IndexModelBase
View model中只保留状态成员的做法,参考了官方文档的 MVVM模式
至此,架构越来越明了了。
Index的完整代码如下

@Entry
@Component
struct Index {
  model: IndexModelInterface = new IndexModel()
  viewModel: IndexViewModel = new IndexViewModel()

  async aboutToAppear() {
    this.viewModel.articles = await this.model.refreshArticles()
  }

  build() {
    Column() {
      IndexContent({ viewModel: this.viewModel })
    }
  }
}

IndexPreviewer的完整代码如下

@Preview
@Component
struct IndexPreviewer {
  model: IndexModelInterface = new IndexModelMock()
  viewModel: IndexViewModel = new IndexViewModel()

  async aboutToAppear() {
    this.viewModel.articles = await this.model.refreshArticles()
  }

  build() {
    IndexContent({ viewModel: this.viewModel })
  }
}

聪明的你一定会想到,这两个struct代码大部分重复,为什么不提取一个基类。别问了,Arkts不支持。@Component struct不支持继承。

Model的实现

最终,真数据和假数据,是在Model里面区分的。
上文中,view model和model都是在界面容器(Index和IndexPreviewer)中持有的。实际上我们``能更进一步,把view model放到model里面。这样,界面容器只和model有耦合,把model里面的view model传到IndexContent里面
IndexModelBase的代码如下

export default abstract class IndexModelBase {
  abstract refreshArticles(): Promise<Article[]>

  viewModel: IndexViewModel = new IndexViewModel()

  async refreshData() {
    this.viewModel.articles = await this.refreshArticles()
  }
}

所以IndexModelBase不声明为接口,因为要持有view model,并对里面的articles进行更新。
接下来,让IndexModelBase的子类去实现具体的refreshArticles()方法。IndexModel中,通过网络请求获取数据,更新articlesIndexModelMock中,硬编码假数据给articles
在上文的两个界面容器中,更新数据变得更简单。
以下是Index的最终完整代码

@Entry
@Component
struct Index {
  model: IndexModelBase

  async aboutToAppear() {
    this.model = new IndexModel()
    this.model.refreshData()
  }

  build() {
    Column() {
      IndexContent({ viewModel: this.model.viewModel })
    }
  }
}

以下是IndexPreviewer的最终完整代码

@Entry
@Component
struct IndexPreviewer {
  model: IndexModelBase

  async aboutToAppear() {
    this.model = new IndexModelMock()
    this.model.refreshData()
  }

  build() {
    Column() {
      IndexContent({ viewModel: this.model.viewModel })
    }
  }
}

7ebde29c85a3faa0ae369e2ea9dd3130.jpeg

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计风格:新拟态,一文掌握特征、应用场景、运用方法

Hello&#xff0c;我是大千UI工场&#xff0c;设计风格是我们新开辟的栏目&#xff0c;主要讲解各类UI风格特征、辨识方法、应用场景、运用方法等&#xff0c;包括新拟态、毛玻璃、奢华、新中式等等&#xff0c;有设计需求&#xff0c;我们也可以接单。 一、新拟态风格定义和发…

Vue练习1:组件开发1(头像组件)

样式预览 注释代码 <template><div class"img-box":style"{ //动态style必须为对象width: size rem,height: size rem}"><imgclass"avatar-img":src"url" //动态url/></div> </templ…

智慧公厕管理软件

随着城市化的不断推进&#xff0c;城市公共设施逐渐完善&#xff0c;其中智慧公厕的建设也在不断提速。智慧公厕作为城市基础设施的重要组成部分&#xff0c;对城市卫生水平提升有着不可忽视的作用。而智慧公厕管理软件更是智慧公厕管理的基础&#xff0c;是公共厕所智慧化管理…

javaweb学习day02(CSS)

一、CSS介绍 1 官方文档 CSS 指的是层叠样式表* (Cascading Style Sheets)地址: https://www.w3school.com.cn/css/index.asp离线文档: W3School 离线手册(2017.03.11 版).chm 2 为什么需要 CSS 在没有 CSS 之前&#xff0c;我们想要修改 HTML 元素的样式需要为每个 HTML …

不具备这十个能力,真不能说是专业的B端系统设计师

B端系统的复杂程度要远远的超过C端&#xff0c;作为这类设计师绝对不能满足于&#xff0c;画个界面&#xff0c;拼一下组件能搞定的&#xff0c;真的需要精心研究&#xff0c;本文列举了十项能力&#xff0c;帮助设计师们针对的提升。 一、什么是B端管理系统设计 B端管理系统设…

6、内网安全-横向移动WmiSmbCrackMapExecProxyChainsImpacket

用途&#xff1a;个人学习笔记&#xff0c;有所借鉴&#xff0c;欢迎指正&#xff01; 前言&#xff1a; 在内网环境中&#xff0c;主机192.168.3.31有外网网卡能出网&#xff0c;在取得该主机权限后上线&#xff0c;搭建web应用构造后门下载地址&#xff0c;利用该主机执行相…

pytorch常用激活函数笔记

1. relu函数&#xff1a; 公式&#xff1a; 深层网络内部激活函数常用这个 import matplotlib.pyplot as pltdef relu_fun(x):if x>0:return xelse:return 0x np.random.randn(10) y np.arange(10)plt.plot(y,x)for i ,t in enumerate(x):x[i] relu_fun(t) plt.p…

lazarus:LCL 嵌入 fpwebview 组件,做一个简单浏览器

从 https://github.com/PierceNg/fpwebview 下载 fpwebview-master.zip 简单易用。 先请看 \fpwebview-master\README.md cd \lazarus\projects\fpwebview-master\demo\lclembed 修改 lclembed.lpr 如下&#xff0c;将 fphttpapp. 注释掉&#xff0c;因为我用不上 a simple…

EXTI外部中断

&#xff1f; 难点&#xff1a;中断向量表、看门狗、NVIC的优先级位&#xff1f;EXTI框图&#xff1f; ------------------------ 中断系统 中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;--->例如&#xff1a;…

IIC--集成电路总线

目录 一、IIC基础知识 1、设计IIC电路的原因&#xff1a; 2、上拉电阻阻值怎么确定 3、IIC分类 4、IIC协议 二、单片机使用IIC读写数据 1、 IIC发送一个字节数据&#xff1a; 2、IIC读取一个字节数据&#xff1a; 一、IIC基础知识 1、设计IIC电路的原因&#xff1a; (…

day42 一个极简动画效果(复习相关属性)

<!DOCTYPE html> <html><head><title>动画页面</title><style>body {font-family: Arial, sans-serif;background-color: #f2f2f2;margin: 0;padding: 0;}.container {max-width: 800px;margin: 0 auto;margin-top: 100px;padding: 20px;b…

【学网攻】 第(29)节 -- 综合实验二

系列文章目录 目录 系列文章目录 文章目录 前言 一、综合实验 二、实验 1.引入 实验目标 实验设备 实验拓扑图 实验配置 实验验证 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 -- 交换机配置聚合端口【学网攻…

结构体实现位段

位段是结构体的一种&#xff0c; 是一种特殊的结构体。 位段可以自己设定元素的内存大小。不具备平台间的移植性。 位段的定义 下面是一个定义的位段 这里成员后面的数字代表比特位。 int a : 1;代表a只占一个比特位。 位段中的元素只能是int&#xff0c; char&#xff0c;…

C++ vector用法

目录 1. vector&#xff1a; 1.1 vector 说明 1.2 vector初始化&#xff1a; 方式1. 方式2. ​编辑方式3. 方式4. 方式5. 1.3 vector对象的常用内置函数使用&#xff08;举例说明&#xff09; pop_back&#xff08;&#xff09; 2. 顺序访问vector的几种方式&#x…

云计算基础-华为存储实验

存储配置流程 创建硬盘域&#xff08;CK&#xff0c;包括热备CK&#xff09;创建存储池&#xff08;CKG、Extent&#xff09;创建LUN、LUN组、将LUN加入LUN组创建主机、主机组、将主机加入主机组创建映射视图&#xff08;LUN组和主机组的映射&#xff09; 1. 创建CK 创建硬盘…

LEETCODE 167. 两数之和 II - 输入有序数组

class Solution { public:vector<int> twoSum(vector<int>& numbers, int target) {vector<int> result;//双指针 -矩阵// for(int i0;i<numbers.size();i){// for(int jnumbers.size()-1;j>i;j--){// if(numbers[i]numbers[j]target)…

【LLM-RAG】BGE M3-embedding模型(模型篇|混合检索、多阶段训练)

note M3-Embedding联合了3种常用的检索方式&#xff0c;对应三种不同的文本相似度计算方法。可以基于这三种检索方式进行多路召回相关文档&#xff0c;然后基于三种相似度得分平均求和对召回结果做进一步重排。 多阶段训练过程&#xff1a; 第一阶段&#xff1a;第一阶段的自…

(08)Hive——Join连接、谓词下推

前言 Hive-3.1.2版本支持6种join语法。分别是&#xff1a;inner join&#xff08;内连接&#xff09;、left join&#xff08;左连接&#xff09;、right join&#xff08;右连接&#xff09;、full outer join&#xff08;全外连接&#xff09;、left semi join&#xff08;左…

电商小程序08调用缓存

目录 1 将信息存入缓存中2 获取登录信息3 退出登录4 发布预览总结 小程序的登录功能里&#xff0c;如果只是将登录信息保存到全局变量中&#xff0c;存在的问题是如果小程序重新打开&#xff0c;用户的登录状态就丢失了。为了解决这个问题&#xff0c;我们需要用到微搭的缓存的…

初识最短路径

一.最短路径的介绍 最短路径是图论和网络分析中一个重要的概念&#xff0c;它指的是在一个图或网络中连接两个节点或顶点的路径中&#xff0c;具有最小权重总和的路径。这个权重可以表示为路径上边或弧的长度、耗费、时间等&#xff0c;具体取决于问题的背景和应用场景。 如果你…