HarmonyOS鸿蒙Next中规范化状态管理,兼容V1和V2的状态管理机制

HarmonyOS鸿蒙Next中规范化状态管理,兼容V1和V2的状态管理机制 书接上回,上一篇文章我们介绍了commondb模块和UMI理念(链接),也提到了规范化状态管理。可能有些开发者还不知道什么是规范化状态管理,正好我们uctoo-app要实现一个回收站的功能,我们就用这个回收站功能的开发过程浅显易懂的示例规范化状态管理,以及兼容V1和V2的状态管理机制。

由于uctoo-app是从MultiBusinessOffice示例程序修改而来,原来的组件都是状态管理V1的,而我们新开发的commondb库都是用的新的状态管理V2。因此就遇到了如何兼容状态管理V1和V2的问题。翻看了几遍官方文档对状态管理V1 V2混用和迁移指导,竟然那么多规则,那么多限制,那么多概念,这是给普通开发者看的?怕是研究生也难以很快研究明白吧!还有什么API19以前,API19以后,好像现在开发者能公开用到的是API 17版本吧,18什么时候发布,19又什么时候发布?

真担心这么搞,鸿蒙生态错过鸿蒙生态最重要的发展时期,我鸿蒙APP都还没开发出来。不慌!高手从来不拘泥于现有条件,在软件世界里我们可以自由创造。上篇文章里我们提到了规范化状态管理库和SQLite没有本质区别,可以用SQLite直接做状态管理库。

首先我们普及一下什么是规范化状态管理,这个概念最早好像是redux的作者还是哪个老外程序员提出来的理念。简而言之,redux-orm之于redux,pinia-orm之于pinia都是从原本没有什么规律的状态管理方案,通过程序约定范式的方式,将状态管理的程序组织方式,转化成看起来像是对关系型数据库进行编程的方式。同时实现了一组类似SQL的算法对状态进行操作。最终实现了状态管理程序遵循了统一的编程风格非常有规律,有利于理解、使用和维护。可以参考使用了pinia-orm的uctoo-admin项目中store目录的程序实现方式,几乎和commondb模块如出一辙。或者换个说法,commondb模块把其他开发框架生态中的状态管理最佳实践在鸿蒙生态实现了一份。好的,理论形成,干就完了!

UCToo的数据库设计规范,约定了用deleted_at字段表示数据是否软删除,值为null即为有效数据,值不为空则是保存的软删除时间。API设计规范,约定了没有对软删除数据进行过滤,都返回给了客户端,由客户端根据deleted_at字段筛选进行显示,保持了较高的灵活性,也可以实现软删除的字段,通过回收站恢复。

我们来实现回收站功能先把原NotesPage里的顶部切换按钮改成桌面和回收站切换,添加一个isRecycleBin字段标识当前数据列表是否回收站状态。

@Component
struct NotesPage {
  @StorageLink('secondBreakPoint') breakPoint: string = '';
  @StorageLink('deviceType') deviceType: string = '';
  @StorageLink('tableList') tableList: string[] = [];
  @StorageLink('selectedTable') selectedTable: string = 'entity';
  @StorageLink('notesNavMode') notesNavMode: NavigationMode = NavigationMode.Split;
  @StorageLink('arrowStatus') arrowStatus: boolean = true;
  @StorageLink('sideBarIsShown') sideBarIsShown: boolean = true;
  @StorageLink('sideBarStatus') sideBarStatus: boolean = this.deviceType === '2in1' ? true : false;
  @StorageLink('secondWindowSizeHeight') windowSizeHeight: number = CommonConstants.COMMON_ZERO;
  @StorageLink('notesPageInfos') notesPageInfos: NavPathStack = new NavPathStack();
  @State singleSelectCapsuleOptions: SegmentButtonOptions = SegmentButtonOptions.capsule({
    buttons: [{ text: $r('app.string.Desktop') }, { text: $r('app.string.RecycleBin') }] as SegmentButtonItemTuple,
    multiply: false,
    fontSize: $r('app.float.font_size_common'),
    fontWeight: FontWeight.Medium,
    fontColor: $r('sys.color.font_secondary'),
    selectedFontColor: $r('app.color.notes_sidebar_color'),
    selectedBackgroundColor: $r('app.color.start_window_background'),
    backgroundBlurStyle: BlurStyle.BACKGROUND_THICK
  });
  @State @Watch('onRecycleChange') singleSelectCapsuleSelectedIndexes: number[] = [0];
  @State isRecycleBin: number = 0;
}

从官方文档看,简单数据类型,可以通过控件参数在V1和V2间进行传递。这个规则简单,咱一个小白程序员也看得懂。控件引用关系是从NotesPage-> EntityListPage->EntityListView,三个控件里都添加isRecycleBin并从参数进行传递。添加了一些界面元素观察isRecycleBin是否如预期传递。

NotesPage.ets
if(this.selectedTable == 'entity'){
  EntityListPage({
    isRecycleBin: this.isRecycleBin
  })
}
if(this.selectedTable == 'uctoo_user'){
  UctooUserListPage()
}
if(this.selectedTable == 'ai_client'){
  AiClientListPage()
}
EntityListPage.ets
Column() {
  Text(`EntityListPage ${this.isRecycleBin}`)
    .fontSize(20)
    .margin(10)
  EntityTitleView();
  EntityListView({isRecycleBin: this.isRecycleBin});
}

从官方文档没有研究明白怎么混用AppStorage和AppStorageV2并在多个不管有没有关系的控件之间共享数据。不慌,我们就用ArkData的关系型数据库自创一套状态管理共享机制,并示例规范化状态管理。

commondb模块设计时就考虑到了支持多数据库,因此只要在rdbDatabase目录新加目录就可以良好组织新增数据库的扩展。新加一个appstore关系型数据库用于保存UI状态。表名起成和控件名称一致,加个Store后缀避免命名冲突。

也就是一个控件对应一张表,一个控件实例对应表中的一行数据,控件中的一个状态对应表的一个列。

@Entity({
  tableName: 'NotesPageStore',
  index: [{ name: 'idIndex', columnName: ['id'], unique: true }]
})
// todo:添加响应式状态关联

export class NotesPageStore {
  //规范化状态管理
  @Columns({name: 'id', type: ColumnType.TEXT, isPrimaryKey: true, autoincrement: false})
  id: string = ''
  @Columns({name: 'isRecycleBin', type: ColumnType.INTEGER })
  isRecycleBin: number  = 0
  @Columns({name: 'privacy_level', type: ColumnType.INTEGER })
  privacy_level: number  = 0
  @Columns({name: 'stars', type: ColumnType.REAL })
  stars: number  = 0
  @Columns({name: 'breakPoint', type: ColumnType.TEXT })
  breakPoint: string | null = ''
  @Columns({name: 'deviceType', type: ColumnType.TEXT })
  deviceType: string | null = ''
  @Columns({name: 'tableList', type: ColumnType.TEXT })
  tableList: string | null = ''
  @Columns({name: 'selectedTable', type: ColumnType.TEXT })
  selectedTable: string | null = 'entity'
  @Columns({name: 'NavigationMode', type: ColumnType.TEXT })
  NavigationMode: string | null = ''
  @Columns({name: 'arrowStatus', type: ColumnType.INTEGER })
  arrowStatus: number | null = 1
  @Columns({name: 'sideBarIsShown', type: ColumnType.INTEGER })
  sideBarIsShown: number | null = 1
  @Columns({name: 'sideBarStatus', type: ColumnType.INTEGER })
  sideBarStatus: number | null = 0
  @Columns({name: 'windowSizeHeight', type: ColumnType.INTEGER })
  windowSizeHeight: number | null = 0
  @Columns({name: 'notesPageInfos', type: ColumnType.TEXT })
  notesPageInfos: string | null = ''
  @Columns({name: 'status', type: ColumnType.INTEGER })

在commondb的Env和RdbUtil中添加appstore数据库的配置和初始化程序。在Index中应用启动时,初始化了两个数据库,一个uctoo保存业务数据,一个appstore保存UI状态数据。在切换回收站状态的toggleRecycleBin方法中通过id=1的一条NotesPageStore数据保存当前UI状态,APP退出下次再启动时,就可以读取这条数据加载回UI界面状态。

(async () => {
  await RdbUtil.getInstance(this.context, Env.DATABASE_UCTOO_NAME).then((rdb) => { //初始化本地关系型数据库,业务数据
    rdb.initRdb();
  });
  await RdbUtil.getAppstoreInstance(this.context, Env.DATABASE_APPSTORE_NAME).then((rdb) => { //初始化本地关系型数据库,UI状态数据
    rdb.initRdb();
  });
})();

至此只是通过约定统一的编程规范,没有新增任何的机制,只用现有的鸿蒙装备库,就轻松实现了兼容V1和V2的状态管理,也许我们可以给这套方案起个名字叫状态管理V1.5或者状态管理V3。学过点编程的程序员应该都能看懂吧,主打的就是简单、简单、还是TMD的简单,只有足够的简单,才能有更多开发者低门槛的参与应用开发,繁荣应用生态。不只简单而且功能还很强大,一套标准的SQL语言支持状态的操作CURD,不用学什么Dispatch、action之类的概念。可能目前的代码看起来还略显冗余,只是构建数据查询的时候代码稍微多了几行,有空了可以给rdbstore第三方库添加一些助手函数,比如getById(),那代码就可以看起来非常简洁了。只从编程的角度来说,一切都非常自然,符合程序员的直觉,程序简洁、优雅、和谐harmony。有条件的专家可以实测一下看性能、安全等其他指标表现如何。

可以看到采用commondb第三方库,只要配置一下数据字段对应关系,添加一些数据加载代码,原有的示例程序代码不论用的什么版本,基本不需要做过多代码重构,就可以改成一个有动态功能,有前后端业务逻辑和运营管理后台的,基本可用的APP。

开发者以此为基础再做进一步的二次开发,开发框架也提供了统一的代码范式,可以简单快捷的CtrlC、CtrlV以及从数据源生成代码,良好的控制了熵增,即使搭建高楼大厦,复杂度也没有显著的增加。欢迎开发者使用commondb通用数据库管理第三方模块,反馈和交流意见建议。


更多关于HarmonyOS鸿蒙Next中规范化状态管理,兼容V1和V2的状态管理机制的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复

感谢楼主分享

更多关于HarmonyOS鸿蒙Next中规范化状态管理,兼容V1和V2的状态管理机制的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙Next的状态管理规范化采用统一API兼容V1和V2机制。V1基于ArkUI的State和Link装饰器实现组件级状态共享,V2引入AppStorage和LocalStorage进行应用级状态管理。两者通过@StorageLink/@StorageProp桥接,AppStorage作为中心化存储,LocalStorage提供页面级隔离。开发者可使用同一套状态装饰器(@State/@Prop等)操作不同层级状态,V2新增@Watch监听状态变化。系统底层自动处理版本差异,业务层无需区分API版本。

在HarmonyOS Next中实现规范化状态管理确实是一个值得探讨的话题。您提出的通过SQLite数据库实现状态管理V1和V2兼容的方案很有创意,这种ORM风格的状态管理确实能带来更好的可维护性和一致性。

关于您提到的具体实现,我有几点技术观察:

  1. 使用RDB作为状态存储的核心优势在于:
  • 天然支持跨组件/页面状态共享
  • 提供持久化能力
  • 支持复杂查询
  • 良好的事务支持
  1. 您实现的NotesPageStore模型将UI状态与数据库表完美映射,这种设计模式类似于Redux-ORM,但直接基于鸿蒙的RDB实现,更加轻量级。

  2. 对于状态管理版本兼容问题,您的方案实际上创建了一个抽象层,屏蔽了底层状态管理实现的差异,这是非常实用的工程实践。

  3. 性能方面需要注意:

  • 频繁的数据库操作可能影响性能
  • 考虑添加适当的缓存层
  • 批量更新优于单条记录更新

这种规范化状态管理方案特别适合:

  • 需要持久化的复杂应用状态
  • 需要跨多个组件共享的状态
  • 需要支持撤销/重做等高级功能的应用

您展示的回收站功能实现很好地验证了这个方案的可行性。期待看到更多基于这种模式的实践案例。

回到顶部