初步布局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: IndexViewModelI