鸿蒙掌上驾考宝典应用开发6:ArkTS 声明式 UI 开发范式——@ComponentV2 实战
·
第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 状态管理原则
- 最小化状态:只在必要时使用 @Local,能用计算属性代替的尽量不用状态
- 单向数据流:数据从父组件通过 @Param 流向子组件,子组件通过 @Event 通知父组件
- 避免深层嵌套:过深的组件嵌套会导致 @Param 传递繁琐,考虑使用全局状态
7.2 性能优化
// 合理使用 @Monitor,避免频繁触发
@Monitor('didCount')
drawProgress() {
// 只在 didCount 真正变化时执行
}
// 使用 @Trace 标记需要观察的字段
@ObservedV2
export class LargeModel {
@Trace criticalField: string = ''; // 只观察关键字段
nonCriticalField: string = ''; // 不观察此字段
}
八、总结
DriverLicenseExam 项目全面采用 V2 组件模式,展示了:
- @ComponentV2:声明式 UI 组件的基础
- @Local / @Param / @Event:组件级状态管理
- @Monitor:响应式监听与副作用
- @ObservedV2 / @Trace:深层对象观察
- 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 示例
更多推荐



所有评论(0)