ArkTS 进阶之道(23):AppStorage 应用级状态边界——为啥所有页面共享状态页面卸载状态仍存在
本文是「ArkTS 进阶之道」系列第 23 篇,续「ArkUI 状态联动」深水区。上篇讲 @Observed/@ObjectLink 嵌套对象观测槽边界(@State 只观测一层嵌套对象子属性改不刷 UI)。本文讲应用级状态边界:AppStorage 应用级全局状态存储——根因在所有页面共享状态,页面卸载状态仍存在。能力系列篇 23 讲过 AppStorage 怎么用,本文讲为哈 AppStorage 应用级状态合法 @State 页面级状态页面卸载就销毁——根因在应用级状态存储槽。
一、开篇:AppStorage 不是页面级 @State,是应用级全局状态存储
你写 React 时,应用级状态是「Context 魔法」(createContext Provider 全局):
// React 应用级状态:Context + Provider const AppContext = createContext({ count: 0 }) function App() { return <AppContext.Provider value={{ count: 0 }}><Page /></AppContext.Provider> } function Page() { const { count } = useContext(AppContext) // 应用级 Context 全局 return <Text>count={count}</Text> } // React 应用级状态:Context + Provider(全局魔法)你写鸿蒙 ArkTS 时,AppStorage应用级全局状态存储——所有页面共享:
// ArkTS AppStorage 应用级全局状态存储 AppStorage.setOrCreate('appCount', 0) // ✅ 初始化 AppStorage 键(应用级全局状态) @Entry @Component struct Index { @StorageLink('appCount') appCount: number = 0 // ✅ @StorageLink 双向绑定 AppStorage 键 build() { Button(`appCount=${this.appCount}`) .onClick(() => { this.appCount++ }) // ✅ @StorageLink 改 → AppStorage 同步 → 其他页面同步 } } // AppStorage 应用级全局状态:所有页面共享,页面卸载状态仍存在Context 魔法 vs 应用级状态存储槽的区别:React 把应用级状态当 Context 魔法(createContext Provider 全局),ArkTS 把 AppStorage 当「应用级状态存储槽」(所有页面共享,页面卸载状态仍存在)。根因不是 Context 魔法是应用级状态存储槽——AppStorage 应用级全局状态存储,所有页面共享,页面卸载状态仍存在。
二、根因:AppStorage 的应用级状态存储槽机制
鸿蒙 ArkUI 的 AppStorage 是应用级状态存储槽绑定——所有页面共享,页面卸载状态仍存在,来自三重绑定机制。
机制 1:AppStorage 应用级全局状态存储——不是页面级 @State
AppStorage 荬饰器应用级全局状态存储——所有页面共享,页面卸载状态仍存在:
// ✅ 初始化 AppStorage(应用级全局状态) AppStorage.setOrCreate('appCount', 0) AppStorage.setOrCreate('appTheme', '#007DFF') @Entry @Component struct Index { @StorageLink('appCount') appCount: number = 0 // ✅ @StorageLink 双向绑定 AppStorage 键 build() { Column() { Text(`appCount = ${this.appCount}`) Button('改 appCount++') .onClick(() => { this.appCount++ }) // ✅ @StorageLink 改 → AppStorage 同步 → 其他页面同步 } } } // AppStorage 应用级全局状态:所有页面共享,页面卸载状态仍存在(不用重新初始化)应用级全局状态存储:AppStorageappCount应用级全局状态存储——所有页面共享appCount,页面卸载状态仍存在(不用重新初始化)。根因不是页面级 @State 是应用级状态存储槽——AppStorage 应用级全局状态存储,所有页面共享,页面卸载状态仍存在。
机制 2:@StorageLink 双向绑定 AppStorage 键——改 Storage 同步其他页面
@StorageLink 荬饰器双向绑定 AppStorage 键——改 @StorageLink → AppStorage 同步 → 其他页面同步:
@Entry @Component struct Index { @StorageLink('appCount') appCount: number = 0 // ✅ @StorageLink 双向绑定 AppStorage 键 @StorageLink('appTheme') appTheme: string = '#007DFF' // ✅ @StorageLink 双向绑定 AppStorage 键 build() { Column() { Text(`appCount = ${this.appCount}`).fontColor(this.appTheme) Button('改 appCount++') .onClick(() => { this.appCount++ }) // ✅ @StorageLink 改 → AppStorage 同步 → 其他页面同步 Button('改 appTheme=红色') .onClick(() => { this.appTheme = '#FF4D4F' }) // ✅ @StorageLink 改 → AppStorage 同同步 } } } // @StorageLink 双向绑定 AppStorage 键:改 → AppStorage 同步 → 其他页面 @StorageLink 同步双向@StorageLink 双向绑定 AppStorage 键:@StorageLinkappCount荬饰器双向绑定 AppStorage 键——改appCount→ AppStorage 同步 → 其他页面@StorageLink同步双向。根因不是页面级状态是应用级状态存储槽——@StorageLink 双向绑定 AppStorage 键,改 → AppStorage 同步 → 其他页面同步双向。
机制 3:AppStorage.set 直接改应用级状态——不用 @StorageLink 也能改
AppStorage.set直接改应用级状态——不用 @StorageLink 也能改,@StorageLink 自动同步:
@Entry @Component struct Index { @StorageLink('appCount') appCount: number = 0 // ✅ @StorageLink 双向绑定 AppStorage 键 build() { Column() { Text(`appCount = ${this.appCount}`) Button('直接改 AppStorage.set("appCount", 100)') .onClick(() => { AppStorage.set('appCount', 100) // ✅ AppStorage.set 直接改应用级状态 // ✅ @StorageLink appCount 自动同步(@StorageLink 绑定 AppStorage 键) }) } } } // AppStorage.set 直接改应用级状态:不用 @StorageLink 也能改,@StorageLink 自动同步AppStorage.set 直接改应用级状态:AppStorage.setappCount直接改应用级状态——不用 @StorageLink 也能改,@StorageLinkappCount自动同步(@StorageLink 绑定 AppStorage 键)。根因不是必须用 @StorageLink 是 AppStorage.set 直接改——AppStorage.set 直接改应用级状态,@StorageLink 自动同步。
机制 4:AppStorage vs @State 页面级状态边界——应用级 vs 页面级
AppStorage应用级全局状态(所有页面共享,页面卸载状态仍存在),@State页面级状态(页面卸载就销毁)——根因都是状态存储但绑的机制不同:
// ✅ AppStorage 路径:应用级全局状态(所有页面共享,页面卸载状态仍存在) AppStorage.setOrCreate('appCount', 0) @Entry @Component struct Page1 { @StorageLink('appCount') appCount: number = 0 // ✅ @StorageLink 双向绑定 AppStorage 键 build() { Button(`Page1 appCount=${this.appCount}`).onClick(() => { this.appCount++ }) } } @Entry @Component struct Page2 { @StorageLink('appCount') appCount: number = 0 // ✅ Page2 也绑定同一个 AppStorage 键 build() { Button(`Page2 appCount=${this.appCount}`).onClick(() => { this.appCount++ }) } } // ✅ AppStorage 应用级全局状态:Page1 改 appCount → Page2 同步(所有页面共享) // ⚠ @State 路径:页面级状态(页面卸载就销毁) @Entry @Component struct Page3 { @State pageCount: number = 0 // ⚠ @State 页面级状态(页面卸载就销毁) build() { Button(`Page3 pageCount=${this.pageCount}`).onClick(() => { this.pageCount++ }) } } // ⚠ @State 页面级状态:Page3 卸载 pageCount 销毁(不共享,不持久)AppStorage vs @State 页面级状态边界:AppStorage 路径——应用级全局状态(所有页面共享,页面卸载状态仍存在,推荐跨页面状态)。@State 路径——页面级状态(页面卸载就销毁,单页面状态)。根因都是状态存储但绑的机制不同——AppStorage 应用级全局状态存储槽(所有页面共享),@State 页面级状态(页面卸载就销毁)。
三、真机配图:AppStorage 应用级状态边界——所有页面共享状态
初始态(appCount=0、appTheme=“#007DFF” 蝔色文字均应用级状态初始态齐):
点调三按钮后(appCount=100 被 AppStorage.set 直接改 100 �覆盖、appTheme=“#FF4D4F” 红色文字、AppStorage 应用级状态同步证据齐):
对比证据:点改 appCount++ �按钮后 appCount 同步(@StorageLink 双向同步),点改 appTheme=红色按钮后 appTheme=“#FF4D4F” 红色文字同步,点直接改 AppStorage.set(“appCount”, 100) 按钮后 appCount=100 被 AppStorage.set 直接改 100 覆盖(@StorageLink 自动同步)。AppStorage 应用级全局状态——所有页面共享,页面卸载状态仍存在,@StorageLink 双向绑定 AppStorage 键,AppStorage.set 直接改应用级状态 @StorageID 自动同步。
四、真解法:AppStorage 应用级状态的三个场景
场景 1:AppStorage 应用级计数器(90% 场景首选,应用级状态存储槽)
AppStorage.setOrCreate('appCount', 0) @Entry @Component struct Index { @StorageLink('appCount') appCount: number = 0 // ✅ @StorageLink 双向绑定 AppStorage 键 build() { Column() { Text(`appCount = ${this.appCount}`) Button('改 appCount++') .onClick(() => { this.appCount++ }) // ✅ @StorageLink 改 → AppStorage 同步 → 其他页面同步 } } }为哈能跑:AppStorage 应用级计数器——应用级状态存储槽,AppStorageappCount应用级全局状态+@StorageLink 双向绑定 AppStorage 键,改appCount→ AppStorage 同步 → 其他页面同步双向。首选这个,90% 的场景应用级跨页面状态用 AppStorage 应用级状态存储槽就够。要写「应用级跨页面状态(计数器等)」时用这个——不用 @State 页面级状态(页面卸载就销毁),AppStorage 应用级状态存储槽所有页面共享。
场景 2:AppStorage 应用级主题色(应用级状态存储槽,所有页面共享主题)
AppStorage.setOrCreate('appTheme', '#007DFF') @Entry @Component struct Index { @StorageLink('appTheme') appTheme: string = '#007DFF' // ✅ @StorageLink 双向绑定 AppStorage 键 build() { Column() { Text(`appTheme = "${this.appTheme}"`).fontColor(this.appTheme) Button('改 appTheme=红色') .onClick(() => { this.appTheme = '#FF4D4F' }) // ✅ @StorageLink 改 → AppStorage 同步 → 其他页面同步 } } } // AppStorage 应用级主题色:所有页面共享 appTheme,改 → AppStorage 同步 → 其他页面同步为哈能跑:AppStorage 应用级主题色——应用级状态存储槽,AppStorageappTheme应用级全局状态+@StorageLink 双向绑定 AppStorage 键,改appTheme→ AppStorage 同步 → 其他页面同步双向。要写「应用级跨页面主题(主题色等)」时用这个——不用 @State 页面级状态(页面卸载就销毁),AppStorage 应用级状态存储槽所有页面共享主题。
场景 3:AppStorage.set 直接改应用级状态(不用 @StorageLink 也能改)
AppStorage.setOrCreate('appCount', 0) @Entry @Component struct Index { @StorageLink('appCount') appCount: number = 0 // ✅ @StorageLink 双向绑定 AppStorage 键 build() { Column() { Text(`appCount = ${this.appCount}`) Button('直接改 AppStorage.set("appCount", 100)') .onClick(() => { AppStorage.set('appCount', 100) // ✅ AppStorage.set 直接改应用级状态 // ✅ @StorageLink appCount 自动同步(@StorageLink 绑定 AppStorage 键) }) } } } // AppStorage.set 直接改应用级状态:不用 @StorageLink 也能改,@StorageLink 自动同步为哈能跑:AppStorage.set 直接改应用级状态——应用级状态存储槽,AppStorage.setappCount直接改应用级状态+@StorageLinkappCount自动同步(@StorageLink 绑定 AppStorage 键)。要写「直接改应用级状态(不用 @StorageLink 也能改等)」时用这个——不用 @StorageLink 也能改,AppStorage.set 直接改应用级状态 @StorageLink 自动同步。
五、一句话哲学
AppStorage 不是页面级 @State,是应用级全局状态存储槽的所有页面共享。ArkUI 的 AppStorage 荬饰器应用级全局状态存储,所有页面共享,页面卸载状态仍存在。根因不是页面级 @State 是应用级状态存储槽——AppStorage 应用级全局状态存储(所有页面共享,页面卸载状态仍存在)+ @StorageLink 双向绑定 AppStorage 键(改 → AppStorage 同步 → 其他页面同步双向)+ AppStorage.set 直接改应用级状态(不用 @StorageLink 也能改 @StorageLink 自动同步)+ AppStorage vs @State 页面级状态边界(应用级 vs 页面级)。对比 React 应用级状态 Context 魔法(createContext Provider 全局),ArkTS AppStorage 应用级状态存储槽所有页面共享。
状态联动深水区串讲:@Watch 绑 @State 变触发副作用回调槽(篇 68)+ @Link $ 语法双向绑定跨组件同步(篇 69)+ @Provide/@Consume 跨层级传递槽祖辈后辈同步(篇 70)+ @Observed/@ObjectLink �嵌套对象观测槽子属性改刷 UI(篇 71)+ AppStorage 应用级状态存储槽所有页面共享(篇 72,@State 页面级状态页面卸载就销毁)——续「ArkUI 状态联动」深水区,讲状态联动深水区(@Watch 副作用回调槽边界 + @Link $ 语法双向绑定边界 + @Provide/@Consume 跨层级传递槽边界 + @Observed/@ObjectLink �嵌套对象观测槽边界 + AppStorage 应用级状态存储槽边界)。
系列预告:下篇(篇 73)讲 LocalStorage 页面级状态边界(页面级局部状态根因),续「ArkUI 状态联动」深水区。五阶段哲学体系:类型哲学(50-52)→ 作用域哲学(53-55)→ 状态哲学(56-59)→ 渎染哲学(60-62)→ 组件设计(63-67)→ 状态联动深水区(68+)讲清 ArkTS/ArkUI 进阶哲学。
能力系列回链
| 能力系列篇 | 本文进阶点 |
|---|---|
| 篇 23 AppStorage 用法 | AppStorage 应用级状态存储槽边界根因(所有页面共享页面卸载状态仍存在) |
| 篇 13 @State 基础用法 | 状态哲学:@State 赋值就刷 UI 依赖追踪 |
| 篇 59 @Watch 用法 | 状态联动深水区:@Watch 副作用回调槽 vs onClick 直接调副作用边界 |
真机 demo 完整代码
// 篇 72 demo:AppStorage 应用级状态边界 // AppStorage 应用级全局状态:所有页面共享,页面卸载状态仍存在 // @StorageLink/@StorageProp 绑定 AppStorage 键 // ✅ 初始化 AppStorage(应用级全局状态) AppStorage.setOrCreate('appCount', 0) AppStorage.setOrCreate('appTheme', '#007DFF') @Entry @Component struct Index { // ✅ @StorageLink 双向绑定 AppStorage 键(应用级全局状态) @StorageLink('appCount') appCount: number = 0 @StorageLink('appTheme') appTheme: string = '#007DFF' @State log: string = '(未操作)' build() { Column({ space: 12 }) { Text('篇 72 配图:AppStorage 应用级状态边界') .fontSize(16).fontWeight(FontWeight.Bold).margin({ top: 16, bottom: 4 }) Text('AppStorage 应用级全局状态:所有页面共享,页面卸载状态仍存在') .fontSize(11).fontColor('#888').margin({ bottom: 8 }) // ✅ 应用级状态展示 Column({ space: 6 }) { Text('应用级全局状态(AppStorage)') .fontSize(12).fontColor('#2563eb').fontWeight(FontWeight.Bold) Text(`appCount = ${this.appCount}`) .fontSize(15).fontWeight(FontWeight.Bold).fontColor('#2563eb') Text(`appTheme = "${this.appTheme}"`) .fontSize(13).fontColor(this.appTheme) } .width('92%').padding(10).backgroundColor('#e0f0ff').borderRadius(8) // ✅ 改 AppStorage 键(@StorageLink 双向同步) Button('改 appCount++(@StorageLink 双向同步)') .width('92%').height(40).fontSize(12) .onClick(() => { this.appCount++ // ✅ @StorageLink 改 → AppStorage 同步 → 其他页面同步 this.log = `改 appCount=${this.appCount}(AppStorage 应用级状态)` }) Button('改 appTheme=红色(@StorageLink 双向同步)') .width('92%').height(40).fontSize(12) .onClick(() => { this.appTheme = '#FF4D4F' // ✅ @StorageLink 改 → AppStorage 同步 this.log = `改 appTheme=红色(AppStorage 应用级状态)` }) // ✅ 直接改 AppStorage(不用 @StorageLink,AppStorage.set 直接改) Button('直接改 AppStorage.set("appCount", 100)') .width('92%').height(40).fontSize(12) .onClick(() => { AppStorage.set('appCount', 100) // ✅ AppStorage.set → @StorageLink 同步 this.log = `直接改 AppStorage.set appCount=100(@StorageLink 同步)` }) Text(`日志:${this.log}`).fontSize(10).fontColor('#333').margin({ top: 4 }) } .width('100%').height('100%').alignItems(HorizontalAlign.Center) } }写鸿蒙 ArkUI 记住:AppStorage 不是页面级 @State 是应用级全局状态存储槽的所有页面共享——AppStorage 荬饰器应用级全局状态存储,所有页面共享,页面卸载状态仍存在。根因不是页面级 @State 是应用级状态存储槽——AppStorage 应用级全局状态存储(所有页面共享,页面卸载状态仍存在)+ @StorageLink 双向绑定 AppStorage 键(改 → AppStorage 同步 → 其他页面同步双向)+ AppStorage.set 直接改应用级状态(不用 @StorageLink 也能改 @StorageLink 自动同步)+ AppStorage vs @State 页面级状态边界(应用级 vs 页面级)。AppStorage 应用级计数器用应用级状态存储槽(首选,90% 场景),AppStorage 应用级主题色用应用级状态存储槽所有页面共享主题,AppStorage.set 直接改应用级状态不用 @StorageLink 也能改 @StorageLink 自动同步。应用级状态存储槽所有页面共享页面卸载状态仍存在是 ArkUI 状态联动深水区核心!