鸿蒙Tab实战03 - build方法200行怎么优化?AttributeModifier模式实战

鸿蒙Tab实战03 - build方法200行怎么优化?AttributeModifier模式实战

build方法200行,样式逻辑和UI结构混在一起。如何实现职责分离,让build方法从200行减少到10行?


一、问题场景:build方法200行,样式逻辑和UI结构混在一起

(本节约3分钟,快速了解问题即可)

1.1 问题的开始

在HarmonyOS UI开发中,我们经常遇到这样的问题:UI组件的build方法变得越来越长,样式逻辑和UI结构混在一起,导致代码难以维护、复用和测试。

1.2 传统方式的痛点

痛点1:build方法过长
typescript 复制代码
// ❌ build方法变得很长,难以阅读
@ComponentV2
export struct NavigationView {
  build() {
    Row() {
      ForEach(this.tabVM.itemList, (item: NavigationItem, index) => {
        Stack() {
          List() {
            ListItem() {
              Image(item.icon)
                .width(this.getIconSize(item, index))
                .height(this.getIconSize(item, index))
                .margin({
                  top: this.getIconMarginTop(item, index),
                  bottom: this.getIconMarginBottom(item, index)
                })
            }
            ListItem() {
              Text(item.name)
                .fontSize(this.getFontSize(item, index))
                .fontColor(this.getFontColor(item, index))
                .fontWeight(this.getFontWeight(item, index))
                .backgroundColor(this.getFontBgColor(item, index))
                .padding(this.getTextPadding(item, index))
            }
          }
          .width(this.getListWidth(item, index))
          .height(this.getListHeight(item, index))
          .backgroundColor(this.getListBgColor(item, index))
          .padding({ bottom: this.getListPaddingBottom(item, index) })
          .onClick(() => this.tabVM.clickTab(index))
        }
        .width(this.getStackWidth(item, index))
        .height(this.getStackHeight(item, index))
        .zIndex(this.tabVM.isActivate(index) ? 1 : 0)
        .onAreaChange((o, n) => {
          // 复杂的尺寸计算逻辑
        })
      })
    }
    .alignSelf(ItemAlign.Start)
    .justifyContent(FlexAlign.SpaceBetween)
    .constraintSize({ minWidth: '100%' })
    .alignItems(VerticalAlign.Bottom)
    .align(Alignment.TopStart)
    .onAreaChange((o, n) => {
      // 复杂的样式计算逻辑
    })
    .backgroundImage(this.getBackgroundImage())
    .backgroundImageSize({ width: '100%', height: '100%' })
  }
}

问题

  • build方法超过200行,难以阅读和维护
  • 样式逻辑和UI结构混在一起,职责不清
  • 每次修改样式都需要在build方法中查找
痛点2:难以复用
typescript 复制代码
// ❌ 样式逻辑无法复用
// 组件A
Row() {
  // ...
}
.alignSelf(ItemAlign.Start)
.justifyContent(FlexAlign.SpaceBetween)
.constraintSize({ minWidth: '100%' })

// 组件B需要相同的样式,只能复制粘贴
Row() {
  // ...
}
.alignSelf(ItemAlign.Start)
.justifyContent(FlexAlign.SpaceBetween)
.constraintSize({ minWidth: '100%' })

问题

  • 样式逻辑无法复用,需要复制粘贴
  • 修改样式需要在多个地方修改
  • 容易出现不一致的问题
痛点3:难以测试

样式逻辑无法独立测试,要测试样式逻辑,必须创建完整的UI组件,测试成本高,难以覆盖所有场景。

痛点4:语法限制
typescript 复制代码
// ❌ builder属性中只能用三元运算符,复杂条件难以表达
Row() {
  // ...
}
.width(
  this.shouldStickTop
    ? this.isActivate(index)
      ? sz(72)  // 激活且吸顶
      : sz(64)  // 未激活且吸顶
    : this.businessType === '内嵌'
    ? sz(160)   // 未吸顶且内嵌
    : sz(136)   // 未吸顶且独立
) // 多层嵌套,可读性差

.margin({
  start: this.shouldStickTop && this.isActivate(index) ? szM(8) : szM(0),
  end: this.shouldStickTop && this.isActivate(index) ? szM(8) : szM(0),
}) // 复杂条件用三元运算符表达困难

问题

  • 语法限制:在builder属性中,框架不允许使用if语句,只能用三元运算符
  • 可读性差:在多分支时使用多层嵌套的三元运算符难以理解
  • 难以维护:复杂条件逻辑难以表达和维护
  • 功能受限:无法使用switch-case、循环、函数调用等常规语法

二、实战案例:AttributeModifier模式

2.1 解决方案设计

AttributeModifier是HarmonyOS框架提供的特性,允许我们将样式逻辑从UI组件中分离出来,独立成Modifier类。

核心思路:

  1. UI组件只负责结构:build方法只关注UI结构
  2. 样式逻辑独立到Modifier:所有样式逻辑都在Modifier中
  3. 通过attributeModifier应用:一行代码应用所有样式
  4. 语法灵活性:Modifier中可以使用if-else、switch-case、循环、函数调用等所有常规语法,不受builder属性的语法限制

2.2 完整实现示例

2.2.1 UI组件(build方法10行)
typescript 复制代码
// ✅ build方法简洁,只负责UI结构
@ComponentV2
export struct NavigationView {
  @Param tabVM: NavigationViewModel = new NavigationViewModel()
  
  build() {
    Row() {
      ForEach(this.tabVM.itemList, (item: NavigationItem, index) => {
        Stack() {
          List() {
            ListItem() {
              Image(item.icon)
            }
            ListItem() {
              Text(item.name)
            }
          }
          .onClick(() => this.tabVM.clickTab(index))
        }
        .attributeModifier(new StackStyleModifier(this.tabVM, item, index))
      })
    }
    .attributeModifier(new RowStyleModifier(this.tabVM))
  }
}

对比

  • 传统方式:build方法200+行,样式逻辑和UI结构混在一起
  • AttributeModifier方式:build方法10+行,只负责UI结构
2.2.2 Row样式Modifier
typescript 复制代码
// Row样式逻辑独立到Modifier
export class RowStyleModifier implements AttributeModifier<RowAttribute> {
  constructor(private tabVM: NavigationViewModel) {}
  
  applyNormalAttribute(instance: RowAttribute): void {
    // 可以使用if-else、switch-case等所有常规语法
    instance.alignSelf(ItemAlign.Start)
    instance.justifyContent(FlexAlign.SpaceBetween)
    instance.constraintSize({ minWidth: '100%' })
    instance.alignItems(VerticalAlign.Bottom)
    instance.align(Alignment.TopStart)
    
    // 复杂条件逻辑,不再需要三元运算符
    if (this.tabVM.shouldStickTop) {
      instance.backgroundImage(this.tabVM.getStickyBackgroundImage())
    } else {
      instance.backgroundImage(this.tabVM.getNormalBackgroundImage())
    }
    
    instance.backgroundImageSize({ width: '100%', height: '100%' })
    
    instance.onAreaChange((o, n) => {
      // 复杂的样式计算逻辑
      this.tabVM.handleAreaChange(o, n)
    })
  }
}
2.2.3 Stack样式Modifier
typescript 复制代码
// Stack样式逻辑独立到Modifier
export class StackStyleModifier implements AttributeModifier<StackAttribute> {
  constructor(
    private tabVM: NavigationViewModel,
    private item: NavigationItem,
    private index: number
  ) {}
  
  applyNormalAttribute(instance: StackAttribute): void {
    // 复杂条件逻辑,使用if-else更清晰
    if (this.tabVM.shouldStickTop) {
      if (this.tabVM.isActivate(this.index)) {
        instance.width(sz(72))
        instance.height(sz(72))
      } else {
        instance.width(sz(64))
        instance.height(sz(64))
      }
    } else {
      if (this.tabVM.businessType === '内嵌') {
        instance.width(sz(160))
        instance.height(sz(160))
      } else {
        instance.width(sz(136))
        instance.height(sz(136))
      }
    }
    
    instance.zIndex(this.tabVM.isActivate(this.index) ? 1 : 0)
    
    instance.onAreaChange((o, n) => {
      // 复杂的尺寸计算逻辑
      this.tabVM.handleStackAreaChange(this.index, o, n)
    })
  }
}
2.2.4 List样式Modifier
typescript 复制代码
// List样式逻辑独立到Modifier
export class ListStyleModifier implements AttributeModifier<ListAttribute> {
  constructor(
    private tabVM: NavigationViewModel,
    private item: NavigationItem,
    private index: number
  ) {}
  
  applyNormalAttribute(instance: ListAttribute): void {
    // 使用switch-case处理多种场景
    switch (this.tabVM.getListStyleType(this.index)) {
      case 'active':
        instance.width(sz(72))
        instance.height(sz(72))
        instance.backgroundColor(Color.Blue)
        break
      case 'inactive':
        instance.width(sz(64))
        instance.height(sz(64))
        instance.backgroundColor(Color.Gray)
        break
      default:
        instance.width(sz(60))
        instance.height(sz(60))
        instance.backgroundColor(Color.Transparent)
    }
    
    instance.padding({ bottom: this.tabVM.getListPaddingBottom(this.index) })
  }
}

2.3 实际效果

使用AttributeModifier模式后,我们获得了以下效果:

  1. build方法从200+行减少到10+行:代码量减少95%
  2. 职责分离清晰:UI结构、样式逻辑、业务逻辑分离
  3. 样式逻辑可复用:Modifier可以在多个组件中复用
  4. 样式逻辑可测试:Modifier可以独立测试,无需创建完整UI组件
  5. 语法灵活性:Modifier中可以使用if-else、switch-case、循环、函数调用等所有常规语法

三、理论分析:职责分离的架构基础

3.1 职责分离的理论价值

单一职责原则

  • 每个类或模块应该只有一个职责
  • UI组件只负责UI结构
  • Modifier只负责样式逻辑
  • ViewModel只负责业务逻辑

开闭原则

  • 对扩展开放,对修改关闭
  • 新增样式只需新增Modifier,无需修改UI组件
  • 修改样式只需修改Modifier,无需修改UI组件

3.2 声明式UI的架构基础

声明式UI的特点

  • 描述"是什么",而非"如何做"
  • UI结构清晰,样式逻辑独立
  • 状态变化自动更新UI

职责分离如何支撑声明式编程

  • UI组件:声明UI结构,描述"是什么"
  • Modifier:声明样式逻辑,描述"如何样式化"
  • ViewModel:管理状态,描述"数据是什么"

3.3 架构价值

可维护性提升

  • 样式逻辑独立,修改不影响UI结构
  • 代码结构清晰,易于理解和维护

可复用性提升

  • Modifier可以在多个组件中复用
  • 样式逻辑可以独立封装和复用

可测试性提升

  • Modifier可以独立测试,无需创建完整UI组件
  • 测试成本低,易于覆盖所有场景

四、整体架构图

AttributeModifier模式的整体架构如下:

graph TB subgraph ViewModel层 F[NavigationViewModel
业务逻辑和状态] end subgraph Modifier层 C[RowStyleModifier
Row样式逻辑] D[StackStyleModifier
Stack样式逻辑] E[ListStyleModifier
List样式逻辑] end subgraph UI组件层 A[NavigationView组件] B[build方法
只负责UI结构] A --> B end %% ViewModel到Modifier的连接 F -->|状态变化| C F -->|状态变化| D F -->|状态变化| E %% Modifier到UI组件的连接 C -->|attributeModifier| B D -->|attributeModifier| B E -->|attributeModifier| B %% 可选:添加UI到ViewModel的反向数据流 B -.->|用户交互事件| F

架构说明

  • UI组件层:只负责UI结构,build方法简洁
  • Modifier层:负责所有样式逻辑,可复用、可测试
  • ViewModel层:负责业务逻辑和状态管理

五、方案能力边界

根据样式复杂度、复用需求、build方法长度等因素,选择合适的方案

考虑使用AttributeModifier的情形:

UI属性存在多个复杂逻辑时

项目中多个组件有共同属性

需要复用和共同维护时

属性数量过多导致build函数臃肿时,例如50行以上

六、总结提升:职责分离的价值

(本节约1分钟,快速总结)

6.1 实践验证理论

在实际项目中,AttributeModifier模式带来了显著的效果:

  • build方法从200+行减少到10+行:代码量减少95%
  • 职责分离清晰:UI结构、样式逻辑、业务逻辑分离
  • 样式逻辑可复用:Modifier可以在多个组件中复用
  • 样式逻辑可测试:Modifier可以独立测试

这些实际效果验证了职责分离架构的理论价值。

6.2 理论指导实践

职责分离理论为实际项目提供了架构方向:

  • 单一职责原则:每个类或模块应该只有一个职责
  • 开闭原则:对扩展开放,对修改关闭
  • 声明式UI:描述"是什么",而非"如何做"

6.3 相互印证

AttributeModifier模式是职责分离的实现方式:

  • 职责分离:UI组件、Modifier、ViewModel各司其职
  • 声明式UI:职责分离支撑声明式编程
  • 架构价值:提升可维护性、可复用性、可测试性

七、下一章预告

在下一章中,我们将探讨:全局工具类架构设计:单例模式的生命周期管理。

通过完整的单例模式设计,我们将看到如何解决生命周期管理、初始化分离、响应式更新等基础设施层面的架构挑战。


说明 :本文中的代码示例均经过脱敏处理,部分实现细节已简化,主要用于演示设计思路和架构理念。代码结构符合HarmonyOS规范,但实际使用时请根据具体业务场景调整,并参考HarmonyOS官方文档和最佳实践。

相关推荐
奋斗的小青年!!2 小时前
Flutter跨平台开发鸿蒙应用实战:OA系统考勤打卡组件深度解析
flutter·harmonyos·鸿蒙
全栈开发圈3 小时前
新书速览|鸿蒙之光HarmonyOS 6应用开发入门
华为·harmonyos
儿歌八万首5 小时前
鸿蒙 ArkUI 实战:沉浸式状态栏的 3 种实现方案
华为·harmonyos
大雷神5 小时前
HarmonyOS中考试模板开发教程
华为·harmonyos
全栈开发圈5 小时前
干货分享|鸿蒙6开发实战指南
人工智能·harmonyos·鸿蒙·鸿蒙系统
鸣弦artha6 小时前
Flutter框架跨平台鸿蒙开发 —— Image Widget 基础:图片加载方式
flutter·华为·harmonyos
奋斗的小青年!!7 小时前
在OpenHarmony上玩转Flutter弹出菜单:我的实战经验分享
flutter·harmonyos·鸿蒙
lili-felicity8 小时前
React Native for OpenHarmony 实战:加载效果的实现详解
javascript·react native·react.js·harmonyos
哈哈你是真的厉害8 小时前
React Native 鸿蒙跨平台开发:BaseConverter 进制转换
react native·react.js·harmonyos
奋斗的小青年!!8 小时前
Flutter跨平台开发:笔记分享功能适配OpenHarmony
flutter·harmonyos·鸿蒙