一、前言

前面学习了页面路由、本地 Preferences 存储、网络请求,多页面开发时常遇到:首页、个人中心、设置页共用用户信息、登录状态,跨页面传参繁琐。AppStorage 是 ArkUI 内置全局状态容器,用来存储应用全局共享数据,一处修改、全页面自动刷新 UI;结合 Preferences 可实现「打开 App 从本地加载→运行时全局读写→退出自动持久化保存」完整方案,是项目全局数据最优方案。

适用场景:登录状态、用户昵称、系统主题色、全局配置、用户头像。

二、基础概念

  1. AppStorage:应用级全局单例仓库,全局唯一,所有页面均可存取;
  2. AppStorage.SetOrCreate(key,defaultVal):没有则创建、已有则取值(最常用);
  3. @StorageLink("key"):组件绑定全局变量,双向绑定,修改变量自动同步全局、全局改动页面自动刷新;
  4. @StorageProp("key"):单向绑定,只能读取全局数据,无法在当前组件修改;
  5. 配合 Preferences:应用启动读取本地数据存入 AppStorage,数据变更同步写入本地,实现永久保存。

三、基础 API 使用示例

3.1 全局注册变量(通常放在入口页面 aboutToAppear)

ets

@Entry
@Component
struct Index {
  aboutToAppear() {
    // 注册全局登录状态、用户名,默认未登录
    AppStorage.SetOrCreate<boolean>("isLogin", false)
    AppStorage.SetOrCreate<string>("userName", "游客")
  }

  build() {
    Column({ space:20 }) {
      Text("首页页面")
      NavRouter()
    }
    .width("100%")
    .height("100%")
    .padding(30)
  }
}

// 子组件:双向绑定全局数据
@Component
struct NavRouter {
  // 双向绑定全局key
  @StorageLink("isLogin") login:boolean
  @StorageLink("userName") name:string

  build() {
    Column({space:10}) {
      Text(this.login ? `欢迎:${this.name}` : "当前游客模式")
        .fontSize(18)
      Button("模拟登录修改全局数据")
        .onClick(()=>{
          // 修改变量=修改全局AppStorage,所有绑定页面同步刷新
          this.login = true
          this.name = "鸿蒙开发工程师"
        })
    }
  }
}

3.2 新页面读取全局数据(路由跳转页面)

ets

import router from '@ohos.router'
@Entry
@Component
struct MinePage {
  // 单向绑定,仅读取,不能修改
  @StorageProp("isLogin") isLogin:boolean
  @StorageProp("userName") uname:string

  build() {
    Column({space:20}) {
      Text("个人中心页面").fontSize(22)
      Text(`登录状态:${this.isLogin?"已登录":"未登录"}`)
      Text(`用户名称:${this.uname}`)
      Button("返回首页").onClick(()=>router.back())
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}

关键特性:首页点击登录修改全局,个人中心页面无需传参、自动同步最新数据。

四、AppStorage 常用原生方法

ets

// 获取全局值
let login = AppStorage.Get<boolean>("isLogin")
// 修改全局值
AppStorage.Set<string>("userName","新名字")
// 删除全局key
AppStorage.Delete("userName")
// 清空全部
AppStorage.Clear()

五、进阶:AppStorage + Preferences 持久化联动(项目核心方案)

业务逻辑:

  1. APP 启动 → Preferences 读取本地保存的用户信息 → 存入 AppStorage;
  2. 页面修改全局用户数据 → AppStorage 变更 → 自动写入 Preferences;
  3. 重启 APP,数据不会丢失。

ets

import preferences from '@ohos.data.preferences'
import common from '@ohos.app.ability.common'

// 全局持久化工具类
class GlobalStoreUtil {
  static context:common.UIAbilityContext
  static FILE_NAME:string = "global_user"

  // 初始化:从本地读取数据存入AppStorage,程序启动调用
  static async initFromLocal() {
    let pre = await preferences.getPreferences(this.context,this.FILE_NAME)
    // 读取本地值,无数据用默认值
    let localLogin = await pre.get("isLogin",false)
    let localName = await pre.get("userName","游客")
    // 存入全局仓库
    AppStorage.SetOrCreate("isLogin",localLogin)
    AppStorage.SetOrCreate("userName",localName)
  }

  // 全局数据变化,保存到本地
  static async saveToLocal() {
    let pre = await preferences.getPreferences(this.context,this.FILE_NAME)
    pre.putSync("isLogin",AppStorage.Get("isLogin"))
    pre.putSync("userName",AppStorage.Get("userName"))
    await pre.flush()
  }
}

@Entry
@Component
struct AppEntry {
  aboutToAppear() {
    // 初始化上下文
    GlobalStoreUtil.context = getContext(this) as common.UIAbilityContext
    // 从本地加载数据进全局
    GlobalStoreUtil.initFromLocal()
  }

  // 绑定全局
  @StorageLink("isLogin") login:boolean
  @StorageLink("userName") name:string

  build() {
    Column({space:25}) {
      Text("全局状态+持久化演示").fontSize(22).fontWeight(FontWeight.Bold)
      Text(`用户:${this.name} | ${this.login?"已登录":"未登录"}`).fontSize(18)

      Button("登录(修改全局并保存本地)")
        .width(220)
        .onClick(async()=>{
          this.login = true
          this.name = "OpenHarmony开发者"
          // 变更持久化
          await GlobalStoreUtil.saveToLocal()
        })

      Button("退出登录(清空全局并保存)")
        .width(220)
        .onClick(async()=>{
          this.login = false
          this.name = "游客"
          await GlobalStoreUtil.saveToLocal()
        })

      Button("跳转个人中心页")
        .width(220)
        .onClick(()=>{
          import router from '@ohos.router'
          router.pushUrl({url:"pages/mine/mine"})
        })
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}

mine 页面(pages/mine/mine.ets)

ets

@Entry
@Component
struct Mine {
  @StorageProp("isLogin") login:boolean
  @StorageProp("userName") name:string

  build() {
    Column({space:15}) {
      Text("个人中心").fontSize(22)
      Text(`登录状态:${this.login}`)
      Text(`用户名:${this.name}`)
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}

效果:登录后关闭应用,再次打开 APP 依旧保留登录状态和用户名。

六、综合实战:登录页网络请求→保存全局 + 本地

结合之前 Fetch 登录接口、Preferences、AppStorage 三合一完整业务:

ets

import preferences from '@ohos.data.preferences'

// 沿用前面封装HttpUtil
class HttpUtil{
  static async post(url:string,data:Object){
    let opt:RequestInit={
      method:"POST",
      headers:{"Content-Type":"application/json"},
      body:JSON.stringify(data)
    }
    let res=await fetch(url,opt)
    return res.json()
  }
}

@Entry
@Component
struct LoginPage {
  @State account:string=""
  @State pwd:string=""
  @StorageLink("isLogin") loginFlag:boolean
  @StorageLink("userName") nick:string

  // 登录接口请求
  async submitLogin() {
    try{
      let res = await HttpUtil.post("https://jsonplaceholder.typicode.com/users",{
        name:this.account,pwd:this.pwd
      })
      // 登录成功,修改全局
      this.loginFlag=true
      this.nick=this.account
      // 持久化本地
      await GlobalStoreUtil.saveToLocal()
      AlertDialog.show({title:"提示",message:"登录成功"})
      import router from '@ohos.router'
      router.back()
    }catch(e){
      AlertDialog.show({title:"提示",message:"登录失败"})
    }
  }

  build() {
    Column({space:20}) {
      TextInput({placeholder:"账号"}).width("90%").onChange(v=>this.account=v)
      TextInput({placeholder:"密码"}).type(InputType.Password).width("90%").onChange(v=>this.pwd=v)
      Button("提交登录").width("90%").backgroundColor("#007DFF").onClick(()=>this.submitLogin())
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}

七、开发规范与使用注意事项

  1. **@StorageLink 双向绑定:** 适合需要修改数据(登录、设置开关); **@StorageProp 单向绑定:** 纯展示页面(个人中心、首页展示用户),防止意外篡改全局;
  2. 全局数据统一管理 key 名称,建议在常量文件统一定义,避免字符串写错;
  3. 复杂对象(用户信息含头像、手机号)也可以存入 AppStorage,直接存 object;
  4. 数据量大复杂数据不用 AppStorage,优先 RDB 关系型数据库;
  5. 应用冷启动统一在入口aboutToAppear从 Preferences 加载全局数据。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐