# @Local装饰器与组件局部状态

一、引言

在HarmonyOS NEXT的ArkUI框架中,@Local装饰器是@ComponentV2体系下管理组件局部状态的核心工具。它替代了传统@Component中的@State装饰器,提供了更清晰的状态声明、更明确的更新触发机制,以及与@Param/@Event装饰器的协同能力。

在"星办OA"企业办公审批项目中,@Local被广泛应用于所有页面的状态管理中——从审批中心的视图切换、筛选条件,到详情页的审批数据加载,再到个人中心的开关状态。本文将深入分析@Local装饰器的使用方式、生命周期和行为特性,并结合项目中的实际代码,探讨组件局部状态管理的最佳实践。

二、@Local装饰器基础

2.1 基本声明

在@ComponentV2中,@Local用于声明组件的局部状态:

@ComponentV2
export struct OfficePage {
  @Local selectedView: string = ApprovalView.PENDING  // 当前选中的视图
  @Local selectedType: string = '全部'                  // 当前选中的审批类型
  @Local keyword: string = ''                          // 搜索关键词
  @Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(ApprovalStore, () => new ApprovalStore())!
}

@Local修饰的变量具有以下特性:

  • 局部作用域:状态属于当前组件实例,其他组件不可直接访问
  • 响应式更新:当值变化时,组件自动重新渲染
  • 初始化时机:在组件创建时初始化

2.2 与@State的对比

在@ComponentV2出现之前,@State是管理局部状态的主要方式。@Local与@State的核心区别:

特性 @State @Local

所属框架 @Component @ComponentV2
声明方式 装饰器 装饰器
更新机制 自动检测 自动检测
与@Param协同 不支持 支持
与@Event协同 不支持 支持

三、@Local在项目中的典型应用

3.1 视图切换状态

在审批中心(OfficePage.ets)中,@Local用于管理视图切换和筛选状态:

@ComponentV2
export struct OfficePage {
  @Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(ApprovalStore, () => new ApprovalStore())!
  @Local selectedView: string = ApprovalView.PENDING  // 当前视图:待我审批
  @Local selectedType: string = '全部'                  // 当前筛选类型
  @Local keyword: string = ''                          // 搜索关键词
}

这三个@Local变量共同决定了审批列表的显示内容:

private getVisibleApprovals(): ApprovalRequest[] {
  return this.store.getFilteredApprovals(this.selectedView, this.selectedType, this.keyword)
}

当用户切换视图、选择类型或输入搜索关键词时,对应的@Local变量发生变化,组件重新渲染,审批列表随之更新。

视图切换逻辑:

@Builder
private buildViewTabs() {
  Row() {
    ForEach(this.views, (view: string) => {
      Column({ space: 7 }) {
        Text(view)
          .fontColor(this.selectedView === view ? '#2459E0' : '#667085')
        Divider()
          .color(this.selectedView === view ? '#3B6FF5' : Color.Transparent)
      }
      .onClick(() => {
        this.selectedView = view  // 更新@Local变量
        this.selectedType = '全部'
        this.keyword = ''
      })
    })
  }
}

3.2 搜索状态管理

在审批中心的搜索栏中,@Local keyword管理搜索关键词:

@Builder
private buildSearchBar() {
  Row({ space: 8 }) {
    Text('⌕')
    TextInput({ text: this.keyword, placeholder: '搜索标题、申请人或审批单号' })
      .onChange((value: string) => this.keyword = value)
    if (this.keyword.length > 0) {
      Text('清除')
        .onClick(() => this.keyword = '')
    }
  }
}

搜索关键词的变化通过@Local的响应式机制,自动触发列表过滤和重新渲染。清除按钮通过将keyword设置为空字符串,恢复完整列表。

3.3 审批详情页状态

在审批详情页(ApprovalDetailPage.ets)中,@Local管理多个状态变量:

@ComponentV2
export struct ApprovalDetailPage {
  @Param approvalId: string = ''
  @Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(ApprovalStore, () => new ApprovalStore())!
  @Local approval: ApprovalRequest = new ApprovalRequest()  // 当前审批数据
  @Local found: boolean = false                              // 是否找到审批
  @Local comment: string = ''                                // 审批意见
}

数据加载状态管理:

aboutToAppear(): void {
  this.reloadApproval()
}

private reloadApproval(): void {
  let item: ApprovalRequest | undefined = this.store.getApproval(this.approvalId)
  if (item === undefined) {
    this.found = false  // 更新@Local变量,渲染"未找到"页面
    return
  }
  this.approval = item  // 更新@Local变量,触发重新渲染
  this.found = true
}

条件渲染控制:

build() {
  NavDestination() {
    if (!this.found) {
      // 未找到审批的提示页面
      Column({ space: 12 }) {
        Text('!')
        Text('未找到这条审批')
        Button('返回').onClick(() => this.pageInfos.pop())
      }
    } else {
      // 审批详情内容
      Column() {
        Scroll() {
          Column({ space: 14 }) {
            this.buildStatusCard()
            this.buildBaseInformation()
            // ...
          }
        }
        this.buildActionBar()
      }
    }
  }
}

foundapproval两个@Local变量协同工作,控制页面的状态切换。当found为false时渲染"未找到"页面,为true时渲染审批详情。

3.4 审批表单状态

在审批创建页(ApprovalCreatePage.ets)中,@Local管理表单输入状态:

@ComponentV2
export struct ApprovalCreatePage {
  @Param approvalType: string = '请假'
  @Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(ApprovalStore, () => new ApprovalStore())!
  @Local selectedType: string = '请假'  // 选中的审批类型
  @Local title: string = ''             // 申请标题
  @Local summary: string = ''           // 业务明细
  @Local reason: string = ''            // 申请事由
}

表单验证逻辑依赖于这些@Local变量:

private confirmSubmit(): void {
  if (this.title.trim().length === 0 || this.summary.trim().length === 0 || this.reason.trim().length === 0) {
    promptAction.showToast({ message: '请填写申请标题、业务明细和申请事由' })
    return
  }
  // 提交逻辑
}

3.5 个人中心开关状态

在个人中心(MinePage.ets)中,@Local管理开关状态:

@ComponentV2
export struct MinePage {
  @Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(ApprovalStore, () => new ApprovalStore())!
  @Local approvalNotificationEnabled: boolean = true   // 待办审批提醒开关
  @Local resultNotificationEnabled: boolean = true     // 审批结果提醒开关
}

开关状态通过Toggle组件控制:

@Builder
private buildToggleSetting(kind: string) {
  Row({ space: 12 }) {
    // 图标和文字
    Toggle({ type: ToggleType.Switch, isOn: this.isToggleEnabled(kind) })
      .onChange((enabled: boolean) => this.setToggleEnabled(kind, enabled))
  }
}

private isToggleEnabled(kind: string): boolean {
  return kind === '待办' ? this.approvalNotificationEnabled : this.resultNotificationEnabled
}

private setToggleEnabled(kind: string, enabled: boolean): void {
  if (kind === '待办') {
    this.approvalNotificationEnabled = enabled
  } else {
    this.resultNotificationEnabled = enabled
  }
}

四、@Local与@Param/@Event的协同

4.1 三者的分工

在@ComponentV2中,@Local、@Param和@Event三者各有分工:

  • @Param:接收父组件传入的参数,只读
  • @Local:管理组件自身的局部状态,可读写
  • @Event:向父组件传递事件,用于反向通信

4.2 协同示例

在审批详情页中,三者协同工作:

@ComponentV2
export struct ApprovalDetailPage {
  @Param approvalId: string = ''          // 父组件传入的审批ID(只读)
  @Local store: ApprovalStore = ...       // 局部状态(可读写)
  @Local approval: ApprovalRequest = ...  // 局部状态(可读写)
  @Local found: boolean = false           // 局部状态(可读写)
  @Local comment: string = ''             // 局部状态(可读写)
  @Consumer('pageInfos') pageInfos: NavPathStack = new NavPathStack()
}

  • approvalId由@Param从父组件接收,用于加载审批数据
  • approvalfoundcomment由@Local管理,用于维护页面状态
  • pageInfos通过@Consumer从祖先组件获取,用于导航操作

五、@Local的生命周期

5.1 初始化

@Local变量在组件创建时初始化,初始值在声明时指定:

@Local selectedView: string = ApprovalView.PENDING  // 初始值为"待我审批"
@Local keyword: string = ''                          // 初始值为空字符串
@Local found: boolean = false                        // 初始值为false

5.2 更新触发

@Local变量的更新触发组件重新渲染:

// 用户点击视图标签 -> 更新selectedView -> 组件重新渲染
.onClick(() => {
  this.selectedView = view
  this.selectedType = '全部'
  this.keyword = ''
})

5.3 销毁

当组件被销毁时,@Local变量的值也随之释放。这意味着@Local状态是组件实例级别的,不会跨实例共享,也不会持久化。

六、最佳实践

6.1 状态粒度控制

@Local变量的粒度应该适中:

  • 太粗:一个对象包含所有状态,局部变化可能触发不必要的渲染
  • 太细:每个变量管理一个独立状态,可能导致大量@Local声明
在"星办OA"项目中,@Local变量的粒度控制得很好——每个变量管理一个明确的状态维度。

6.2 与AppStorageV2的配合

对于需要跨页面共享的状态,使用AppStorageV2连接全局仓库;对于页面内的临时状态,使用@Local管理:

@Local store: ApprovalStore = AppStorageV2.connect<ApprovalStore>(...)  // 全局共享
@Local keyword: string = ''                                               // 页面局部

6.3 条件渲染

使用@Local变量控制条件渲染,可以实现页面状态的切换:

if (!this.found) {
  // 加载失败状态
} else {
  // 正常显示状态
}

七、总结

@Local装饰器是@ComponentV2体系中管理组件局部状态的核心工具。它提供了响应式的状态管理能力,当变量值变化时自动触发组件重新渲染。

在"星办OA"项目中,@Local被广泛应用于所有页面:审批中心的视图切换(selectedView)、筛选类型(selectedType)和搜索关键词(keyword),审批详情页的数据加载状态(found)和审批意见(comment),审批创建页的表单输入(title、summary、reason),个人中心的开关状态(approvalNotificationEnabled、resultNotificationEnabled)等。

@Local与@Param/@Event的协同分工,使得组件状态管理既清晰又灵活——@Param接收外部输入,@Local管理内部状态,@Event向外部输出。这种设计模式,是构建可维护、可测试的企业级应用的基础。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐