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

    鸿蒙应用开发中,我们经常会遇到大量属性频繁更新的场景,比如动画效果、拖拽交互、实时数据展示等。传统的属性更新方式存在一些性能瓶颈:

传统方案的痛点

方案 问题描述
状态变量 前端状态管理计算量过大,需要对单个组件进行全量属性更新
AttributeModifier 虽然支持按需更新属性,但仍会采用diff和reset策略,可能带来性能问题

AttributeUpdater的解决方案

AttributeUpdater 作为一个特殊的 AttributeModifier,不仅继承了AttributeModifier的所有功能,还提供了:

  • 直接获取属性对象的能力

  • 直接更新对应属性,无需经过状态变量

  • 支持自定义更新策略,进一步提升属性更新性能

  • 支持直接更新组件的构造参数

提示:AttributeUpdater提供了较高的灵活性,但无法限制"单一数据源"的规则。在与状态变量同时更新同一属性时,存在相互覆盖的情况,必须确保属性设置的合理性。

二、接口定义

2.1 类定义

export declare class AttributeUpdater<T, C = Initializer<T>>
    implements AttributeModifier<T> {
    
    // AttributeModifier接口方法
    applyNormalAttribute?(instance: T): void;
    
    // 初始化方法:首次绑定时触发
    initializeModifier(instance: T): void;
    
    // 属性对象:直接操作组件属性
    get attribute(): T | undefined;
    
    // 构造参数更新器
    public updateConstructorParams: C;
}

2.2 泛型参数

  • T:组件的属性类型(如 ButtonAttributeTextAttribute 等)

  • C:组件的构造参数类型,默认为 Initializer<T>

三、使用示例

3.1 通过modifier直接修改属性

实现自定义AttributeUpdater
// MyButtonModifier.ets
import { AttributeUpdater } from '@kit.ArkUI';

class MyButtonModifier extends AttributeUpdater<ButtonAttribute> {
    
    // 首次绑定时触发initializeModifier方法,进行属性初始化
    initializeModifier(instance: ButtonAttribute): void {
        console.log('初始化按钮属性');
        instance.backgroundColor('#2787D9')
            .width('50%')
            .height(30)
            .borderRadius(15)
            .fontColor('#FFFFFF')
            .fontSize(16);
    }
    
    // 可选:复写applyNormalAttribute实现自定义逻辑
    applyNormalAttribute(instance: ButtonAttribute): void {
        // 如果不调用super,将失去获取attribute属性对象的能力
        super.applyNormalAttribute(instance); // 保持默认行为
        // 可以在这里添加额外的逻辑
    }
}
在页面中使用
// AttModifier.ets
import { MyButtonModifier } from './MyButtonModifier';

@Entry
@Component
struct UpdaterDemo {
    // 创建AttributeUpdater实例
    modifier: MyButtonModifier = new MyButtonModifier();

    build() {
        Row() {
            Column() {
                Button('Button')
                    .attributeModifier(this.modifier)  // 绑定modifier
                    .onClick(() => {
                        // 关键:通过attribute直接修改组件属性
                        // 会立即触发组件属性更新,无需状态变量
                        this.modifier.attribute?.backgroundColor('#17A98D')
                            .width('30%')
                            .borderRadius(20)
                            .fontSize(18);
                        
                        console.log('属性已直接更新');
                    })
            }
            .width('100%')
        }
        .height('100%')
    }
}

执行流程:

  1. 首次渲染:触发 initializeModifier,设置初始样式

  2. 点击按钮:通过 attribute 直接修改属性,立即更新UI

  3. 无需状态变量,无需等待下一次刷新周期

3.2 更新组件的构造参数

AttributeUpdater 还可以直接更新组件的构造参数,这是普通AttributeModifier做不到的。

// MyTextModifier.ets
import { AttributeUpdater } from '@kit.ArkUI';

class MyTextModifier extends AttributeUpdater<TextAttribute, TextInterface> {
    
    initializeModifier(instance: TextAttribute): void {
        // 可以留空,或者设置一些初始属性
        instance.fontColor('#333333');
    }
}

// AttUpdate.ets
@Entry
@Component
struct UpdaterDemo {
    modifier: MyTextModifier = new MyTextModifier();

    build() {
        Row() {
            Column() {
                Text('初始文本')
                    .attributeModifier(this.modifier)
                    .fontColor(Color.White)
                    .fontSize(14)
                    .border({ width: 1, color: '#CCCCCC' })
                    .textAlign(TextAlign.Center)
                    .lineHeight(20)
                    .width(200)
                    .height(50)
                    .backgroundColor('#2787D9')
                    .onClick(() => {
                        // 直接更新组件的构造参数
                        // 相当于创建了一个新的Text组件
                        this.modifier.updateConstructorParams('更新后的文本');
                        
                        // 也可以传递多个参数
                        // this.modifier.updateConstructorParams('新文本', 100);
                    })
            }
            .width('100%')
        }
        .height('100%')
    }
}

运行效果:

  • 初始显示:"初始文本",蓝色背景,白色文字

  • 点击后:文本内容变为"更新后的文本",其他样式保持不变

3.3 多个属性的链式更新

// 复杂动画示例
class AnimatedButtonModifier extends AttributeUpdater<ButtonAttribute> {
    
    initializeModifier(instance: ButtonAttribute): void {
        instance.backgroundColor('#2787D9')
            .width(200)
            .height(60)
            .borderRadius(8)
            .fontSize(16)
            .fontWeight(FontWeight.Medium);
    }
    
    // 动画效果方法
    startAnimation() {
        let step = 0;
        const targetWidth = 300;
        const startWidth = 200;
        
        // 模拟动画帧
        const animationId = setInterval(() => {
            step += 10;
            const newWidth = startWidth + (targetWidth - startWidth) * (step / 100);
            
            // 直接更新宽度属性
            this.attribute?.width(newWidth);
            
            if (step >= 100) {
                clearInterval(animationId);
                // 动画结束时改变颜色
                this.attribute?.backgroundColor('#17A98D');
            }
        }, 16); // 60fps
    }
}

四、使用原则

4.1 生命周期触发时机

class DemoModifier extends AttributeUpdater<ButtonAttribute> {
    
    // 首次绑定时触发(只触发一次)
    initializeModifier(instance: ButtonAttribute): void {
        console.log('1. 初始化属性');
        instance.backgroundColor('#2787D9');
    }
    
    // 当modifier作为状态变量变化时触发
    applyNormalAttribute(instance: ButtonAttribute): void {
        console.log('2. 应用normal属性');
        super.applyNormalAttribute(instance); // 保持attribute对象的能力
    }
}

@Entry
@Component
struct Demo {
    @State modifier: DemoModifier = new DemoModifier(); // 作为状态变量

    build() {
        Button('Test')
            .attributeModifier(this.modifier)
            .onClick(() => {
                // 修改状态变量会触发 applyNormalAttribute
                this.modifier = new DemoModifier(); 
                
                // 直接修改attribute不会触发 applyNormalAttribute
                this.modifier.attribute?.backgroundColor('#FF0000');
            })
    }
}

4.2 限制说明

规则 说明 示例
一对一组装 一个AttributeUpdater对象只能同时关联一个组件 modifier同时给两个Button使用,只有最后一个生效
super调用必须 复写applyNormalAttribute时必须调用super,否则失去attribute能力 super.applyNormalAttribute(instance)
数据源冲突 与状态变量同时更新同一属性会相互覆盖 同时使用@Stateattribute修改背景色

4.3 错误示例及修正

// 错误:没有调用super
class WrongModifier extends AttributeUpdater<ButtonAttribute> {
    applyNormalAttribute(instance: ButtonAttribute): void {
        // 没有调用super
        instance.backgroundColor('#FF0000'); // 直接设置
        // attribute对象将变为undefined
    }
}

// 正确:调用super
class CorrectModifier extends AttributeUpdater<ButtonAttribute> {
    applyNormalAttribute(instance: ButtonAttribute): void {
        super.applyNormalAttribute(instance); // 保持attribute能力
        // 可以添加额外逻辑
        console.log('属性已应用');
    }
}

// 错误:多个组件使用同一个modifier
const modifier = new MyButtonModifier();
Button('按钮1').attributeModifier(modifier); // 生效
Button('按钮2').attributeModifier(modifier); // 覆盖按钮1,只有按钮2生效

// 正确:每个组件使用独立的modifier
Button('按钮1').attributeModifier(new MyButtonModifier());
Button('按钮2').attributeModifier(new MyButtonModifier());

五、与AttributeModifier的对比

能力 AttributeModifier AttributeUpdater
多态样式支持 支持 支持
跨文件复用 支持 支持
业务逻辑编写 支持 支持
直接获取属性对象 不支持 支持
直接更新属性 不支持 支持
更新构造参数 不支持 支持
无需状态变量 需要 支持
性能开销 有diff/reset策略 更低(直接更新)

注意事项

  1. 一对一组装:一个AttributeUpdater对象只能关联一个组件

  2. super调用:复写applyNormalAttribute时必须调用super

  3. 初始化顺序:首次绑定时才会触发initializeModifier

  4. 属性对象判空:使用attribute前需要判空

选型

是否更新组件属?
├── 否 → 使用普通属性方法
└── 是 → 
    ├── 是否需要跨文件复用?
    │   ├── 否 → 考虑使用 @Styles/@Extend
    │   └── 是 → 使用 AttributeModifier
    │       └── 需要高性能频繁更新吗?
    │           ├── 否 → 普通 AttributeModifier
    │           └── 是 → AttributeUpdater
    └── 需要更新构造参数吗?
        └── 是 → 必须使用 AttributeUpdater

Logo

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

更多推荐