本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

   鸿蒙开发中,数据传递机制是状态管理和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.namethis.age

  • 父组件的nameage改变时,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刷新又要修改参数的用回调传递

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐