HarmonyOS UI架构探索(续) 原创

大黑布林李子
发布于 2024-2-4 00:32
浏览
2收藏

作者:大李子
团队:坚果派
十年iOS,All in转鸿蒙

前言

首先感谢大家的热情,几天之内我的上一篇文章《HarmonyOS UI架构探索》就上了社区头条,并从此多了个头衔。并且有许多小伙伴积极地在留言区讨论这个架构的设计。其中一位小伙伴的建议非常合理,他说的设计,和我最初的设想是一样的。但是由于还不太熟悉Arkts的语法,遇到了一点麻烦,阻碍了我设想的实现。不过之后我又尝试了一下,发现没问题了。
经过我反复的对比,最终发现了之前无法调用view model里面刷新方法的原因。将在下文详细描述。
现在我们就给这个架构优化一下。

大致思路

其实我最初的设想没那么复杂,就是把IndexUI里的内容提出来,创建一个IndexContent,然后用Index去包一层,用于真实数据运行。另外再创建一个IndexPreviewer包一层IndexContent用于预览。
这样的话IndexContent只依赖一个状态变量viewModel,所有的数据和刷新操作都在viewModel里面。然后让Index传入带真实数据的IndexViewModel,让IndexPreviewer传入带假数据的IndexViewModelMock,这两个view model用一个接口或基类来控制它们的实现。
这里补充一下为什么之前这样设计架构会遇到刷新方法调用报错。经过对比调试,发现当IndexIndexPreviewer(以下简称界面容器)传view model的时候,不能在参数里面传new的实例。必须让界面容器先持有view model,声明一个成员,再把这个成员传进去。
那么接下来就开始动刀吧,把繁琐的架构简化一下。

去掉本不愿意加的model层

上一版的设计是让model层去进行数据获取,然后model层有接口,子类去决定使用真数据还是假数据。现在它们已经不需要了。但是删之前先等会,代码还在里面。
先给IndexViewModel创建一个接口IndexViewModelInterface

export default interface IndexViewModelInterface {
  articles: Array<Article>
  title: string
  refreshData()
}

IndexViewModel继承该接口

@Observed
export default class IndexViewModel implements IndexViewModelInterface {
  articles: Array<Article> = []
  title: string = "1"

  async refreshData(): Promise<void> {
    this.articles = await this.refreshArticles()
    this.title = "2"
    return new Promise(resolve => { resolve() })
  }

  async refreshArticles(): Promise<Article[]> {
    let articles: Array<Article>
    // 异步请求,返回文章列表
    return new Promise(resolve => {
      resolve(articles)
    })
  }
}

声明一个IndexViewModelMock继承该接口

@Observed
export default class IndexViewModelMock implements IndexViewModelInterface {
  articles: Array<Article> = []
  title: string = "1"

  async refreshData(): Promise<void> {
    this.articles = await this.refreshArticles()
    this.title = "2"
    return new Promise(resolve => { resolve() })
  }

  refreshArticles(): Promise<Article[]> {
    return new Promise(resolve => {
      const articles = [/*硬编码的文章假数据*/]
      resolve(articles)
    })
  }
}

好了,之前的model类现在可以删掉了。

界面容器的更改

Index界面使用真数据,这是更改后的代码

@Entry
@Component
struct Index {
  viewModel: IndexViewModelInterface = new IndexViewModel()

  async aboutToAppear() {
    this.viewModel.refreshData()
  }

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

IndexPreviewer使用假数据,这是更改后的代码

@Entry
@Component
struct IndexPreviewer {
  viewModel: IndexViewModelInterface = new IndexViewModelMock()

  async aboutToAppear() {
    this.viewModel.refreshData()
  }

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

此处用Index再示范一下,之前调用刷新方法报错的时候是怎么写的

@Entry
@Component
struct Index {
  build() {
    Column() {
      IndexContent({ viewModel: new IndexViewModel() }) // 传入一个未被本实例持有的实例,并在IndexContent里调用refreshData(),将报错并crash。
    }
  }
}

完整代码请参考这个commit

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2024-2-4 00:34:20修改
4
收藏 2
回复
举报
1条回复
按时间正序
/
按时间倒序
wx65b0afa1cee7b
wx65b0afa1cee7b

大佬牛逼,顺便看下私信呗

已于2024-2-5 10:25:18修改
回复
2024-2-5 10:24:36
回复
    相关推荐