鸿蒙应用开发之父子组件传参:@Param、@Event、@Once 装饰器详解与实战

这是一个使用鸿蒙技术开发的本地原生记账应用,非常适合大家用来练手。相关源码已上传至 Github,点击此处查看项目。欢迎大家交流、指正,也欢迎提交 PR。

一、引言

在鸿蒙(HarmonyOS)应用开发中,状态管理是构建动态、可交互界面的核心。随着 API 12 及后续版本对 @ComponentV2 装饰器的引入,状态管理变得更加简洁和高效。其中,@Param 装饰器是子组件接收父组件数据的关键桥梁。

二、什么是 @Param?

@Param 是鸿蒙状态管理 V2 版本(@ComponentV2)中用于接收外部传入数据的装饰器。它的核心作用是让子组件能够从父组件获取单向同步 的数据。当父组件的数据源发生变化时,子组件中对应 @Param 装饰的变量会自动更新并刷新 UI。

2.1 核心特点

  • 单向数据流 :数据只能从父组件流向子组件,子组件不能直接修改 @Param 变量(只读)。若需要修改,可搭配 @Event@Once 使用。
  • 灵活初始化 :支持本地默认值,但若从父组件传入了值,则优先使用外部传入的值。未初始化时必须配合 @Require 使用,强制要求外部传入。
  • 广泛的数据类型支持 :可以装饰 numberstringbooleanObjectclassArraySetMapDate 等,也支持 nullundefined 和联合类型。
  • 简化 V1 状态管理 :相比 V1 的 @Prop@Link@ObjectLink 等装饰器,@Param 使用方式更统一,减少了选择困难和性能问题。

三、基础用法:从父组件传递数据

下面通过一个简单的计数器示例,展示 @Param 的基本用法。

3.1 父组件:定义状态并传递

父组件使用 @Local 定义本地状态,并通过子组件的构造参数将数据传递下去。

typescript 复制代码
// 父组件
@Entry
@ComponentV2
struct Parent {
  @Local count: number = 0;

  build() {
    Column() {
      Text(`父组件 count: ${this.count}`)
        .fontSize(20)
      Button('增加')
        .onClick(() => {
          this.count++;
        })
      Child({ value: this.count })   // 将 count 传入子组件
    }
    .width('100%')
    .height('100%')
  }
}

3.2 子组件:使用 @Param 接收数据

子组件使用 @Require @Param 声明一个必须从外部传入的属性。

typescript 复制代码
// 子组件
@ComponentV2
struct Child {
  @Require @Param value: number;   // 必须从外部传入

  build() {
    Column() {
      Text(`子组件接收的 value: ${this.value}`)
        .fontSize(20)
        .onClick(() => {
          // ❌ Cannot assign to 'value' because it is a read-only property. <ArkTSCheck>
          // this.value = 1;
        })
    }
  }
}

3.3 运行效果

每次点击父组件中的"增加"按钮,count 增加,子组件中 value 的显示会随之更新。

重点 :子组件内无法对 this.value 重新赋值(尝试赋值会报编译错误),保证了数据流的单向性和可预测性。

上面示例中传递的是基本数据类型,如需传递引用数据类型,需要配合 @ObservedV2 + @Trace

四、进阶用法:@Once 与 @Event

4.1 @Once:仅初始化时接收

若只需在初始化时接收一次值,后续不再随父组件更新,可搭配 @Once 使用。

@Once 是鸿蒙状态管理 V2 中与 @Param 配合使用的装饰器,用于标记某个参数仅在组件初始化时接收一次 父组件传入的值。一旦组件创建完成,即使父组件的数据源后续发生变化,子组件中 @Once 装饰的变量也不会再更新,从而避免不必要的 UI 刷新,提升性能。

typescript 复制代码
@ComponentV2
struct Child {
  @Param @Once initialValue: string = "默认值";

  build() {
    Text(this.initialValue)
  }
}

4.2 @Event:子组件通知父组件修改

若需要在子组件内修改外部数据,可通过 @Event 装饰回调函数,让子组件"通知"父组件执行修改,从而保持数据和 UI 的一致性。

@Event 是鸿蒙状态管理 V2 中用于定义事件回调的装饰器。它允许父组件将方法作为回调传递给子组件,子组件通过调用该回调来"通知"父组件执行修改操作,从而在保持单向数据流的前提下实现子组件对父组件数据的间接修改。

typescript 复制代码
// 父组件
@Entry
@ComponentV2
struct Parent {
  @Local count: number = 0;

  // 定义一个修改 count 的方法
  incrementCount() {
    this.count++;
  }

  build() {
    Column() {
      Text(`父组件 count: ${this.count}`)
      Child({ value: this.count, onIncrement: () => {
        this.incrementCount()
      } })
    }
  }
}

// 子组件
@ComponentV2
struct Child {
  @Require @Param value: number;
  @Require @Event onIncrement: () => void; // 接收一个事件回调

  build() {
    Column() {
      Text(`子组件 value: ${this.value}`)
      Button('子组件请求增加')
        .onClick(() => {
          this.onIncrement(); // 调用父组件的方法
        })
    }
  }
}

五、使用提示与最佳实践

  • 优先使用 @Param :在 V2 架构中,@Param 是子组件接收外部数据的首选方式,它比 V1 的 @Prop 更简洁、性能更好。
  • 配合 @Require 使用 :如果 @Param 变量没有设置默认值,必须加上 @Require 装饰器,强制父组件传入该参数,避免运行时错误。
  • 避免在子组件内修改 @Param :保持数据流的单向性,有助于代码的可维护性和可预测性。如需修改,请使用 @Event 回调。
  • 合理使用 @Once :对于只需要初始化一次的数据(如配置项、静态文本),使用 @Once 可以避免不必要的 UI 刷新,提升性能。

六、总结

@Param 是鸿蒙 @ComponentV2 状态管理体系中的基石。它通过强制单向数据流,让组件间的数据传递变得清晰、可预测。结合 @Once@Event,你可以构建出既灵活又健壮的组件树。

相关推荐
ClouGence1 小时前
一个人录好的测试用例,团队怎么一起用?
前端·测试
无限压榨切图仔1 小时前
从 Claude Code 切到 Codex:我用 Agent、Skills、MCP 做完了一个内容运营工具
前端·后端
濮水大叔2 小时前
NestJS 与 CabloyJS 的 env/config 架构对比:从环境变量到实例级配置
前端·node.js·nestjs
成都渲染101云渲染66662 小时前
Blender渲染时,纯CPU渲染的设置教程
前端·javascript·blender
董员外2 小时前
RAG 系统进化论(一):纵览 RAG 的发展历程
前端·人工智能·后端
FF2501_940228582 小时前
HarmonyOS应用开发实战:猫猫大作战-AVPlayer 音视频播放
harmonyos·鸿蒙
Gauss松鼠会2 小时前
【GaussDB】GaussDB锁阻塞源头查询
java·开发语言·前端·数据库·算法·gaussdb·经验总结
YHHLAI3 小时前
Vue 3 流式输出实战:从零掌握 LLM Streaming 与 SSE 协议
前端·javascript·vue.js
2601_965798473 小时前
Arrow Unlocker HTML5 Game Review: Keep Web Arcade Players Coming Back
前端·html·html5