记一次component和storage生命周期不统一导致的问题

133 阅读2分钟

现象

进入sdk页面后,点击页面中的发送指令进入loading状态,切到非sdk页面再切回sdk页面,此时sdk页面显示比正常进入sdk页面异常

原因

app集成sdk页面时,切换tab时重现初始化了sdk页面。会重新走aboutApear从storage里面拿数据来刷新ui。在出现问题的场景时,发送指令后appStorege的一个属性被修改,重新创建view时,该数据没有恢复,所以不对应。

由于问题出现复杂业务里面,逻辑嵌套比较多,要一层一层分析。如果用一个demo来说明就是component生命周期和该页面数据生命周期没有对应。

分析:

一、storage的定义:

AppStorage是应用全局的UI状态存储,是和应用的进程绑定的,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储。

和AppStorage不同的是,LocalStorage是页面级的,通常应用于页面内的数据共享。而AppStorage是应用级的全局状态共享。

二:demo简化分析

下面是一个抽象出来的简单demo

import { router } from '@kit.ArkUI'let localStorage = new LocalStorage()
​
@Entry({ storage: localStorage })
@Component
struct TestPage {
  @LocalStorageLink("storage_text") localStorage_text: string = '默认'
  @StorageProp("storage_text") appStorage_text: string = "默认"
​
  aboutToAppear(): void {
    console.log("aboutToAppear")
  }
​
  aboutToDisappear(): void {
    console.log("aboutToDisappear")
    // AppStorage.setOrCreate("storage_text", "默认")
  }
​
  build() {
    Column() {
      Text(this.localStorage_text)
      // Text(this.appStorage_text)
        .fontSize(30)
        .fontColor(Color.Red)
        .onClick(() => {
          this.localStorage_text = '被点击'
          // AppStorage.setOrCreate("storage_text", "被点击")
        })
        .gesture(LongPressGesture().onAction(() => {
          router.replaceUrl({url:"pages/TestPage"})
        }))
    }.width('100%')
    .height('100%')
    .alignItems(HorizontalAlign.Center)
  }
}

如果使用appStorage来保存数据,在click触发时,text被修改。这时长按进入一个新的页面(模拟页面重新创建),text不是预期的“默认”。

原因就是数据和view的生命周期不一致导致的,理论上view重新创建,数据也应该恢复默认值。但是这里因为用了appStorage,一个是页面生命周期,一个是app生命周期,当然就没法对应。

三、解决办法

那么解决方案有2个方向:

1.ui和数据生命周期对应。系统提供了页面级别的storage:LocalStorage,它在页面重新创建时也会重新初始化。

2.在ui销毁时,重置appStorage的数据。demo上就是在aboutApear是把appStorage中的text重置。

四、延伸

appStorage和localStorage各有优势,appStorage在跨页面,跨模块通信时使用非常方便。这边修改,那边去观察即可。

localStorage在跨页面通信没那么方便,但是生命周期和页面一致,同生共死,不用去额外处理生命周期不一致导致的bug。

良好的app体验应该在创建ui时,借助持久化数据以及云端数据,尽量恢复上一次的状态。比如项目中的发送指令后的loading状态,在杀掉app再重新打开app时,如果还处于loading状态要恢复对应的ui。