【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. 同步引用时注意 type 。AppStorage.SetOrCreate 的类型要和 @StorageLink 声明一致,类型对不上会有隐式转换问题。

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

相关推荐
李游Leo7 小时前
HarmonyOS 7 DualCart 平行视界适配实录 04:EasyGo × 虚拟容器:商品比价双详情与分栏比例策略【鸿蒙心迹】
华为·harmonyos
Alice-YUE7 小时前
RAF 渲染帧速通:从渲染帧到性能优化
性能优化
李游Leo7 小时前
HarmonyOS 7 PixelBridge 原生库适配实录 06:Release 性能基线、资源释放、包体积与工程化验收【鸿蒙心迹】
华为·harmonyos
传奇开心果编程8 小时前
【ArkUI进阶练中学】第11课:安全与合规进阶
学习·ui·华为·harmonyos
熊猫钓鱼>_>9 小时前
Kotlin Multiplatform for OpenHarmony 实战:为 Reaktive 实现响应式原语适配(完整版 · 含摘要目录与技术图表)
华为·kotlin·大模型·ai编程·harmonyos·适配·reaktive
李游Leo12 小时前
HarmonyOS 7 + Spatial Recon Kit-C++:3DGS 高斯参数的非有限值隔离与可渲染性门禁【鸿蒙心迹】
开发语言·c++·3d·harmonyos
熊猫钓鱼>_>13 小时前
Kotlin Multiplatform for OpenHarmony 实战:为 MVIKotlin 实现单向数据流适配
华为·ai编程·鸿蒙·openharmony·适配·数据流·mvkotlin
传奇开心果编程13 小时前
【ArkUI进阶练中学】第12课:应用架构演进与遗留系统迁移
学习·ui·华为·harmonyos
海盗123414 小时前
微软技术日报 2026-10-04:26H2 三个已知问题确认,Blazor 补上智能体 UI
人工智能·microsoft·ui·机器人·aigc
无名猿14 小时前
移动构造与移动赋值:把资源偷过来
c++·性能优化·内存管理·现代c++·语法基础