discuz论坛模板,搜索广告优化,网店运营推广具体内容,ae素材网初步布局Index
当我们新建一个工程之后#xff0c;首先会进入Index页。我们先简单的做一个文章列表的显示
class Article {title?: stringdesc?: stringlink?: string
}Entry
Component
struct Index {State articles: Article[] []build() {Row() {Scroll() {Column() …初步布局Index
当我们新建一个工程之后首先会进入Index页。我们先简单的做一个文章列表的显示
class Article {title?: stringdesc?: stringlink?: 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?: ArrayArticlerefreshData() {// 请求网络数据// 更新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()在接口里运行时调用会报错。于是我们再把articles和refreshData()分开refreshData()放到一个新建的类IndexModel中。 这样IndexPreviewer和Index里面依赖的成员都是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(): PromiseArticle[]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: IndexModelBaseasync aboutToAppear() {this.model new IndexModel()this.model.refreshData()}build() {Column() {IndexContent({ viewModel: this.model.viewModel })}}
}
以下是IndexPreviewer的最终完整代码
Entry
Component
struct IndexPreviewer {model: IndexModelBaseasync aboutToAppear() {this.model new IndexModelMock()this.model.refreshData()}build() {Column() {IndexContent({ viewModel: this.model.viewModel })}}
}