第6篇:ArkTS 声明式 UI 开发范式——@ComponentV2 实战

在这里插入图片描述

一、引言

HarmonyOS 从 API 12 开始推出了 V2 组件模式,引入了 @ComponentV2@Local@Param@Event@Monitor 等新装饰器,相比传统的 @Component 模式,V2 模式提供了更简洁的响应式编程体验。DriverLicenseExam 项目全面采用了 V2 组件模式,本文将从源码角度进行深度解析。

二、V2 组件核心装饰器

2.1 @ComponentV2——声明 V2 组件

// 使用 @ComponentV2 替代 @Component
@ComponentV2
export struct HomeView {
  // 组件内容
}

@ComponentV2 相比 @Component 的优势:

  • 更简洁的装饰器语法
  • 更好的响应式性能
  • 原生支持 @Monitor 监听
  • 与 @ObservedV2 / @Trace 深度集成

2.2 @Local——本地响应式状态

@Local 装饰的变量是组件的本地状态,当其值变化时,UI 会自动更新:

@ComponentV2
export struct HomeView {
  @Local currentTab: number = 0;  // 当前 tab 索引
  @Local guideData: GuideData = GuideData.instance;
  @Local videoList: Video[] = this.examService.getVideList();
}

使用场景:

  • 组件内部状态,不需要外部传入
  • 状态变化需要触发 UI 更新
  • 初始值在组件内部确定

2.3 @Param——外部传入参数

@Param 装饰的变量由父组件传入,类似 Vue 的 props:

// 子组件定义
@ComponentV2
export struct HomeView {
  @Param didCount: number = 0;       // 已练习题目数
  @Param totalCount: number = 0;     // 总题目数
  @Event tabChange: (index: number) => void = () => {};  // 事件回调
}

// 父组件传参
HomeView({
  didCount: this.didCount,
  totalCount: this.totalCount,
  tabChange: (index: number) => {
    this.updateForm(index);
  }
});

@Param 的特点:

  • 默认值设置在子组件中
  • 父组件可以传入新值覆盖
  • 值变化时自动触发子组件更新

2.4 @Event——事件回调

@Event 是 V2 模式中专门用于事件回调的装饰器:

@ComponentV2
export struct HomeView {
  @Event tabChange: (index: number) => void = () => {};
  
  // 在组件内部触发事件
  onClick(() => {
    this.tabChange(this.currentTab);
  })
}

@Event 与 @Param 回调的区别:

特性 @Event @Param 回调函数
语义 明确表示事件 参数传递
默认值 空函数 需手动设置
类型安全 强类型 强类型

三、@Monitor——响应式监听

@Monitor 装饰器可以监听 @Local 或 @Param 变量的变化,执行相应的逻辑:

@ComponentV2
struct HomeView {
  @Param didCount: number = 0;
  @Param totalCount: number = 0;

  @Monitor('didCount')
  drawProgress() {
    // 当 didCount 变化时,重新绘制进度条
    const width = this.context.width;
    const height = this.context.height;
    this.context.clearRect(0, 0, width, height);
    
    const doneWidth = width * this.didCount / this.totalCount;
    this.context.beginPath();
    this.context.strokeStyle = '#64BB5C';
    this.context.lineWidth = 2;
    this.context.moveTo(0, 5);
    this.context.lineTo(doneWidth, 5);
    this.context.stroke();
  }
}

多变量监听:

// 监听 guideData 对象的 driveStage 属性变化
@Monitor('guideData.driveStage')
dirveStage() {
  this.currentTab = this.guideData.driveStage as number - 1;
  this.tabChange(this.currentTab);
}

四、@ObservedV2 / @Trace——深度观察

对于复杂对象,使用 @ObservedV2 装饰类,@Trace 装饰属性,实现深层响应式:

// 定义可观察的数据模型
@ObservedV2
export class ExamService {
  private static _instance: ExamService;
  
  @Trace mockExamCount: number = 0;    // 模拟考试次数
  @Trace mockExamScore: number[] = [];  // 模拟考试分数
}

@ObservedV2
export class GuideData {
  private static _instance: GuideData;
  @Trace city: string = '北京';
  @Trace licenseType?: number;
  @Trace driveStage?: number;
}

@ObservedV2 + @Trace 的使用场景:

  • 跨组件的共享数据模型
  • 单例服务的数据状态
  • 需要深层监听的对象属性

五、V2 组件完整示例

以下是一个完整的 V2 组件示例,展示了所有装饰器的配合使用:

// 数据模型
@ObservedV2
export class UserModel {
  @Trace name: string = '';
  @Trace age: number = 0;
  @Trace isVIP: boolean = false;
}

// 子组件
@ComponentV2
export struct UserCard {
  @Param user: UserModel = new UserModel();
  @Local isExpanded: boolean = false;
  @Event onVIPClick: () => void = () => {};

  @Monitor('user.isVIP')
  onVIPStatusChange() {
    console.log(`VIP status changed to: ${this.user.isVIP}`);
  }

  build() {
    Column() {
      Text(this.user.name)
        .fontSize(18)
        .fontWeight(FontWeight.Bold);

      if (this.isExpanded) {
        Text(`年龄: ${this.user.age}`);
        Button('开通会员')
          .onClick(() => this.onVIPClick());
      }
    }
    .onClick(() => {
      this.isExpanded = !this.isExpanded;
    });
  }
}

// 父组件
@Entry
@ComponentV2
struct MainPage {
  @Local currentUser: UserModel = new UserModel();

  aboutToAppear() {
    this.currentUser.name = '张三';
    this.currentUser.age = 25;
  }

  build() {
    Column() {
      UserCard({
        user: this.currentUser,
        onVIPClick: () => {
          this.currentUser.isVIP = true;
        }
      });
    }
  }
}

六、V1 与 V2 的对比

特性 V1 (@Component) V2 (@ComponentV2)
装饰器 @State, @Prop, @Link @Local, @Param, @Event
监听 @Watch @Monitor
对象观察 @Observed + @ObjectLink @ObservedV2 + @Trace
语法 较复杂 更简洁直观
性能 一般 更优

七、V2 模式的最佳实践

7.1 状态管理原则

  1. 最小化状态:只在必要时使用 @Local,能用计算属性代替的尽量不用状态
  2. 单向数据流:数据从父组件通过 @Param 流向子组件,子组件通过 @Event 通知父组件
  3. 避免深层嵌套:过深的组件嵌套会导致 @Param 传递繁琐,考虑使用全局状态

7.2 性能优化

// 合理使用 @Monitor,避免频繁触发
@Monitor('didCount')
drawProgress() {
  // 只在 didCount 真正变化时执行
}

// 使用 @Trace 标记需要观察的字段
@ObservedV2
export class LargeModel {
  @Trace criticalField: string = '';  // 只观察关键字段
  nonCriticalField: string = '';      // 不观察此字段
}

八、总结

DriverLicenseExam 项目全面采用 V2 组件模式,展示了:

  1. @ComponentV2:声明式 UI 组件的基础
  2. @Local / @Param / @Event:组件级状态管理
  3. @Monitor:响应式监听与副作用
  4. @ObservedV2 / @Trace:深层对象观察
  5. AppStorage / AppStorageV2:全局状态共享

V2 模式是鸿蒙应用开发的未来方向,推荐新项目优先采用。


关键源码文件:

  • products/entry/src/main/ets/pages/home/HomeView.ets — V2 组件示例
  • products/entry/src/main/ets/pages/practice/PracticeView.ets — V2 组件示例
  • commons/datasource/src/main/ets/ExamService.ets — @ObservedV2 示例
  • commons/commonLib/src/main/ets/utils/AccountUtil.ets — @ObservedV2 示例
Logo

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

更多推荐