【HarmonyOS开发小实践】ArkUI 应用级状态AppStorage 与跨页面共享、持久化

组件内状态管不好跨页面的数据

@State@Prop@Link 解决的是单个组件或父子组件之间的数据问题。但真实应用里,用户登录状态、全局配置、购物车这些数据要跨多个页面共享,甚至重启后还要保留。这就要用到应用级状态管理。

应用级状态分三个:LocalStorage(页面级)、AppStorage(全局)、PersistentStorage(持久化)。

LocalStorage:页面范围内的共享

LocalStorage 让一个页面(及其子组件树)共享状态,作用域限制在页面内:

typescript 复制代码
// 创建 LocalStorage 实例
let storage = LocalStorage.GetShared();  // 获取共享实例
storage.setOrCreate('pageTitle', '购物车');

@Entry(storage)
@Component
struct CartPage {
  build() {
    Column() {
      Text('购物车页面')
    }
  }
}

@Entry(storage) 把 LocalStorage 绑定到页面入口,页面及其子树都能访问。适合"页面级的数据,离开页面就销毁"的场景。

AppStorage:全局共享

AppStorage 是进程级的全局状态,所有页面和组件都能读写。这是最常用的跨页面共享方案:

typescript 复制代码
// 在任意地方设置全局状态
AppStorage.SetOrCreate('userId', '10086');
AppStorage.SetOrCreate('isLoggedIn', true);

@Entry
@Component
struct HomePage {
  @StorageLink('userId') userId: string = '';  // 双向绑定到 AppStorage
  @StorageProp('isLoggedIn') isLoggedIn: boolean = false;  // 单向读取

  build() {
    Column({ space: 16 }) {
      Text(`当前用户:${this.userId}`)
      Text(`登录状态:${this.isLoggedIn}`)

      Button('切换用户')
        .onClick(() => {
          this.userId = '10087';  // 修改,全局同步
        })
    }
  }
}

两种访问方式:

  • @StorageLink:双向绑定,改本地变量会同步到 AppStorage,其他页面也更新
  • @StorageProp:单向读取,只读全局值,本地修改不影响全局
复制代码
AppStorage['userId'] = '10086'
        │
        ├── @StorageLink userId ──> 组件A(可读写,改了同步回全局)
        │                          组件A 改 userId='10087'
        │                             │
        │                             └──> AppStorage 更新
        │                                   └──> 组件B 的 userId 也变
        │
        └── @StorageProp userId ──> 组件C(只读,改了不影响全局)

PersistentStorage:持久化,重启不丢

AppStorage 是内存态,应用重启就清空。PersistentStorage 把它备份到磁盘,重启后恢复:

typescript 复制代码
// 把 AppStorage 的 userId 持久化到磁盘
PersistentStorage.PersistProp('userId', '10086');
// 或持久化 isLoggedIn
PersistentStorage.PersistProp('isLoggedIn', true);

PersistProp(key, value) 的作用是:把 AppStorage 中的 key 关联到持久化存储,设置初始值。之后:

  • 修改 AppStorage 里的 userId,会自动持久化
  • 应用重启,userId 从磁盘恢复

典型用法------登录状态持久化:

typescript 复制代码
// 应用启动时(如 EntryAbility 的 onCreate)
onCreate() {
  PersistentStorage.PersistProp('loginToken', '');  // 初始为空
}

// 登录成功后
function onLoginSuccess(token: string) {
  AppStorage.SetOrCreate('loginToken', token);  // 自动持久化
}

// 任何页面读取
@StorageLink('loginToken') loginToken: string = '';
if (this.loginToken) {
  // 已登录
} else {
  // 未登录
}

三者关系图

复制代码
PersistentStorage(磁盘,持久化)
        │ PersistProp 关联
        ↓
AppStorage(内存,全局共享)
        │ SetOrCreate / @StorageLink / @StorageProp
        ↓
组件(各页面读写)

LocalStorage(页面级,独立于上面两条线)
        │ @Entry(storage)
        ↓
页面及子树组件

关键理解:PersistentStorage 不是独立存储,它是 AppStorage 的持久化备份。数据同时存在内存(AppStorage)和磁盘(PersistentStorage),内存态读写快,磁盘态保证重启不丢。

完整案例:购物车跨页面共享

购物车数据多个页面都要用,是典型的应用级状态场景:

typescript 复制代码
// 商品对象
@Observed
class CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
  constructor(id: number, name: string, price: number, quantity: number) {
    this.id = id;
    this.name = name;
    this.price = price;
    this.quantity = quantity;
  }
}

// 应用启动时初始化购物车
PersistentStorage.PersistProp('cartItems', []);

// 商品列表页:加入购物车
@Entry
@Component
struct GoodsPage {
  @StorageLink('cartItems') cartItems: CartItem[] = [];

  build() {
    List() {
      ForEach([1, 2, 3], (id: number) => {
        ListItem() {
          Row() {
            Text(`商品 ${id}`)
            Blank()
            Button('加入购物车')
              .onClick(() => {
                this.cartItems.push(new CartItem(id, `商品${id}`, 9.9, 1));
                // @StorageLink 修改,全局同步 + 自动持久化
              })
          }
        }
      }, (id: number) => id.toString())
    }
  }
}

// 购物车页:展示并修改
@Entry
@Component
struct CartPage {
  @StorageLink('cartItems') cartItems: CartItem[] = [];

  build() {
    Column() {
      Text(`共 ${this.cartItems.length} 件商品`)
      List() {
        ForEach(this.cartItems, (item: CartItem) => {
          ListItem() {
            Row() {
              Text(item.name)
              Text(`¥${item.price}`)
              Text(`x${item.quantity}`)
            }
          }
        }, (item: CartItem) => item.id.toString())
      }
    }
  }
}

商品页加购物车 → cartItems 更新 → 购物车页自动刷新 → 同时持久化到磁盘。这个链路覆盖了应用级状态的完整用法。

@Provide/@Consume:跨层级共享的另一选择

前面讲应用级状态时提过 @Provide/@Consume,它也是共享机制,但作用域是组件树而非全进程:

typescript 复制代码
// 祖先组件提供
@Component
struct Ancestor {
  @Provide('theme') theme: string = 'light';  // 提供共享数据

  build() {
    // 很深层的子组件
    GrandChild()
  }
}

// 深层子组件消费(不用中间逐层传)
@Component
struct GrandChild {
  @Consume('theme') theme: string;  // 消费共享数据

  build() {
    Text(`当前主题:${this.theme}`)
  }
}

@Provide/@Consume 适合组件树内的跨层级共享(如主题、语言),AppStorage 适合全应用共享(如登录态)。前者随组件树销毁而回收,后者进程级常驻,按生命周期选。

小小总结一下

  1. 区分数据生命周期再选型 。页面内临时数据用 @State,组件树内共享用 @Provide/@Consume,全局共享用 AppStorage,要持久化再加 PersistentStorage。别所有数据都往 AppStorage 里塞。

  2. PersistProp 的 key 要统一管理。持久化 key 散落各处容易冲突,建议集中定义常量。

  3. 同步引用时注意 typeAppStorage.SetOrCreate 的类型要和 @StorageLink 声明一致,类型对不上会有隐式转换问题。

  4. 持久化数据只存可序列化的PersistentStorage 存的是可序列化数据,函数、回调、资源引用别往里放,恢复时会是空或异常。

相关推荐
FlashGeek2 小时前
全 AI 实战: 一次语音房 Flutter内核 native 内存泄露的定位与修复
性能优化
昇腾知识体系3 小时前
vLLM-Ascend 支持矩阵:supported_models 模型列表、BF16/ACLGraph 支持项核对方法
人工智能·华为·知识图谱·vllm
万物智能信息科技4 小时前
板载按键key的ADC转换和信号控制—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
嵌入式硬件·华为·开源·harmonyos·鸿蒙
undsky_5 小时前
传统色 × 现代和弦:专为 UI/UX 打造的国风配色 Skill
ui·ai·aigc·ai编程·ux
威哥爱编程6 小时前
HarmonyOS 6.1 端侧 3DGS 重建实战:重建在 C 层,ArkTS 只管"看"和"改"
华为·harmonyos·arkts
威哥爱编程6 小时前
HarmonyOS 6.1 沉浸光感实战:接口路径选错,代码不报错、页面没效果
harmonyos
wuyk5556 小时前
从零吃透 MQTT 通信|第 11 章 MQTT 项目调试验证、性能优化、常见疑难问题、OTA 升级基础
c语言·开发语言·stm32·学习·性能优化
贾伟康6 小时前
【HarmonyOS 7新能力|014】冷启网络预建链入门实战:从能力边界到最小可运行链路
harmonyos·arkts·启动优化·网络优化·harmonyos 7
Sunny_G6 小时前
CodeMirror 6 代码块渲染踩坑:一个空行毒死全文档(鸿蒙编辑器卡片化/折叠/点击进源码)
ai编程·harmonyos