鸿蒙中的 引用传递 和 值传递
本文详细介绍了HarmonyOS开发中的数据传递机制,重点对比了值传递和引用传递的特性与应用场景。值传递通过复制数据副本实现静态展示,而引用传递通过共享内存地址支持UI动态刷新。文章还介绍了API 20+新增的回调传递方式,既能刷新UI又可修改参数。针对不同开发场景,作者提供了明确的传递方式选择建议:静态数据用值传递,动态数据用引用传递,需要修改参数的则使用回调传递。同时指出了常见的开发误区,为开
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
鸿蒙开发中,数据传递机制是状态管理和UI刷新的关键。特别是在使用@Builder、@Component等装饰器时,选择正确的传递方式直接影响UI的响应性和应用性能。
一、基础
1.1 什么是值传递?
值传递是指将变量的实际值复制一份传递给函数或组件。传递后,原变量和接收方各自拥有独立的数据副本,互不影响。
通俗理解:就像你复印了一份文件交给别人,别人在复印件上修改不会影响你的原件。
1.2 什么是引用传递?
引用传递是指将变量的内存地址(引用)传递给函数或组件。传递后,原变量和接收方指向同一块内存数据,任何一方修改都会影响另一方。
通俗理解:就像你把原文件的存储位置告诉别人,别人直接去这个位置修改文件,你看到的也是修改后的内容。
二、在鸿蒙开发中的体现
2.1 值传递示例
在鸿蒙中,最常见的值传递就是普通参数的传递:
@Builder
function showUserInfo(userName: string, userAge: number) {
Column() {
Text(`姓名:${userName}`)
Text(`年龄:${userAge}`)
}
}
@Entry
@Component
struct ParentComponent {
@State name: string = '张三';
@State age: number = 25;
build() {
Column() {
// 这里是值传递
showUserInfo(this.name, this.age)
Button('修改信息')
.onClick(() => {
this.name = '李四'; // UI会刷新
this.age = 30;
// 但showUserInfo内部的UI不会刷新!
})
}
}
}
特点:
-
传递的是
this.name和this.age的值 -
父组件的
name和age改变时,showUserInfo内部的UI不会自动刷新
2.2 引用传递示例
鸿蒙中通过对象字面量形式实现引用传递:
// 定义一个包装类
class UserInfo {
public name: string = '';
public age: number = 0;
}
@Builder
function showUserInfoRef(params: UserInfo) {
Column() {
Text(`姓名:${params.name}`)
Text(`年龄:${params.age}`)
}
}
@Entry
@Component
struct ParentComponent {
@State user: UserInfo = { name: '张三', age: 25 };
build() {
Column() {
// 这里是引用传递(对象字面量形式)
showUserInfoRef({ name: this.user.name, age: this.user.age })
Button('修改信息')
.onClick(() => {
this.user.name = '李四'; // UI会刷新
this.user.age = 30;
// showUserInfoRef内部的UI也会自动刷新!
})
}
}
}
特点:
-
传递的是对象的引用
-
父组件的
user属性改变时,showUserInfoRef内部的UI会自动刷新
备注:@Builder的引用传递必须使用对象字面量形式,这是ArkUI框架的语法要求。
三、两种传递方式的对比
3.1 核心区别
| 对比维度 | 值传递 | 引用传递 |
|---|---|---|
| 传递内容 | 数据的副本 | 数据的地址 |
| 内存占用 | 多份数据副本 | 共享同一份数据 |
| 修改影响 | 互不影响 | 互相影响 |
| UI刷新 | 不触发 | 会触发 |
| 适用场景 | 静态数据 | 需要动态刷新的数据 |
3.2 代码对比
// 值传递:各自独立
let a = 10;
let b = a; // b得到的是10的副本
b = 20; // 修改b
console.log(a); // 输出:10(a不受影响)
// 引用传递:共享数据
let obj1 = { value: 10 };
let obj2 = obj1; // obj2得到的是obj1的引用
obj2.value = 20; // 修改obj2
console.log(obj1.value); // 输出:20(obj1也被改变)
四、在@Builder中的实际应用
4.1 场景一:静态展示(适合值传递)
@Builder
function StaticCard(title: string, content: string) {
Column() {
Text(title).fontSize(20)
Text(content).fontSize(16)
}
.padding(10)
.backgroundColor('#f5f5f5')
.borderRadius(8)
}
@Entry
@Component
struct StaticDemo {
build() {
Column() {
// 静态数据,不需要刷新,用值传递就够了
StaticCard('公告', '系统将于今晚22:00升级')
StaticCard('提醒', '请及时保存数据')
}
}
}
4.2 场景二:动态数据(必须用引用传递)
class DynamicData {
public title: string = '';
public count: number = 0;
}
@Builder
function DynamicCounter(params: DynamicData) {
Column() {
Text(params.title).fontSize(18)
Text(`计数:${params.count}`).fontSize(24)
Button('增加')
.onClick(() => {
// 注意:这里不能直接修改params
// 需要通过事件回调让父组件修改
})
}
}
@Entry
@Component
struct DynamicDemo {
@State data: DynamicData = { title: '点击计数器', count: 0 };
build() {
Column() {
// 引用传递,实现动态刷新
DynamicCounter({ title: this.data.title, count: this.data.count })
Button('增加计数')
.onClick(() => {
this.data.count++; // UI自动刷新
})
}
}
}
五、按回调传递(API 20+)
从API version 20开始,鸿蒙提供了更强大的传递机制——按回调传递,通过UIUtils.makeBinding()实现。
5.1 基础用法
import { Binding, MutableBinding, UIUtils } from '@kit.ArkUI';
@Builder
function AdvancedCounter(
readOnlyCount: Binding<number>, // 只读引用
readWriteCount: MutableBinding<number> // 可读写引用
) {
Column() {
Text(`只读计数:${readOnlyCount.value}`)
Text(`可读写计数:${readWriteCount.value}`)
Button('修改可读写计数')
.onClick(() => {
readWriteCount.value += 1; // 可以直接修改
})
}
}
@Entry
@ComponentV2
struct AdvancedDemo {
@Local count1: number = 0;
@Local count2: number = 0;
build() {
Column() {
AdvancedCounter(
// Binding类型:只读
UIUtils.makeBinding<number>(() => this.count1),
// MutableBinding类型:可读写
UIUtils.makeBinding<number>(
() => this.count2,
(val: number) => {
this.count2 = val; // 必须提供setter
}
)
)
Button('修改只读计数')
.onClick(() => {
this.count1++; // UI刷新
})
}
}
}
5.2 三种传递方式对比
| 特性 | 值传递 | 引用传递 | 回调传递 |
|---|---|---|---|
| UI刷新 | ❌ | ✅ | ✅ |
| 修改参数 | ❌ | ❌ | ✅ |
| 多个参数 | ✅ | ❌(只支持1个) | ✅ |
| 版本要求 | API 7+ | API 7+ | API 20+ |
六、常见误区
6.1 误区一:认为对象传递都是引用传递
// 错误理解
@Builder
function wrongWay(obj: any) {
// 以为这是引用传递,但实际上可能是值传递
}
// 正确做法
class Wrapper {
public data: any;
}
@Builder
function rightWay(params: Wrapper) {
// 这才是真正的引用传递
}
6.2 误区二:试图在@Builder内修改引用参数
// 错误做法
@Builder
function badBuilder(params: { value: number }) {
Button('点击')
.onClick(() => {
params.value++; // 运行时错误!
})
}
// 正确做法
@Builder
function goodBuilder(
params: MutableBinding<{ value: number }>
) {
Button('点击')
.onClick(() => {
params.value.value++; // 使用MutableBinding
})
}
6.3 总结
| 场景 | 推荐传递方式 | 原因 |
|---|---|---|
| 静态文案 | 值传递 | 简单高效 |
| 单个状态变量 | 引用传递 | UI自动刷新 |
| 需要修改参数 | 回调传递 | 安全可控 |
| 多个状态变量 | 合并为对象+引用传递 | 避免参数个数限制 |
如何选择传递方式?
// 1. 静态数据 → 值传递
@Builder
function StaticUI(text: string) { }
// 2. 单个状态变量需要刷新 → 引用传递
@Builder
function DynamicUI(params: { value: string }) { }
// 3. 需要内部修改数据 → 回调传递
@Builder
function InteractiveUI(
data: MutableBinding<{ value: string }>
) { }
// 4. 复杂业务逻辑 → 自定义组件
@Component
struct ComplexUI {
@State data: any;
// 完整的生命周期和状态管理
}
开发中:
-
值传递:传递数据副本,适合静态数据,性能好但不支持UI刷新
-
引用传递:传递数据地址,适合动态数据,支持UI刷新但不能修改参数
-
回调传递:API 20+新增,既支持UI刷新又支持参数修改
原则:
-
不需要UI刷新的用值传递
-
需要UI刷新的用引用传递
-
既要UI刷新又要修改参数的用回调传递
更多推荐
所有评论(0)