













本文档基于 HarmonyOS NEXT API 12+ 标准,详细阐述 ArkTS 中 struct 的定义、核心特性、使用规范、最佳实践及性能优化策略,为企业级应用开发提供统一的技术标准。
struct 是 ArkTS 中专门用于定义**声明式UI组件**的核心结构类型,是 ArkUI 组件化开发的基础载体。所有自定义UI组件都必须通过 struct 定义,与 @Component 装饰器配合使用,构成 ArkTS 声明式开发的核心单元。
• 组件化封装:提供UI逻辑与状态的封装单元,实现高内聚低耦合的组件设计
• 声明式语法支持:与声明式UI范式深度融合,支持UI = f(state)的开发模式
• 性能优化:固定内存布局,支持AOT预编译优化,提升UI渲染性能
• 类型安全:编译期验证组件结构与属性,减少运行时错误
核心规则
ArkTS 中自定义UI组件必须使用 struct 定义,不支持使用 class 定义UI组件;class 仅用于定义纯逻辑类与数据模型。
标准的自定义组件由 @Component 装饰器、struct 关键字、属性定义和 build() 方法组成:
/**
* 自定义按钮组件
* @description 通用操作按钮,支持多种样式配置
*/
@Component // 标记为UI组件
struct CustomButton {
// 组件属性定义
@Prop label: string = "确定"; // 按钮文本
@Prop buttonType: ButtonType = ButtonType.Normal; // 按钮类型
@Prop onClick: () => void; // 点击回调
// 组件生命周期
aboutToAppear() {
// 组件即将显示时执行
console.log(`按钮 ${this.label} 即将显示`);
}
aboutToDisappear() {
// 组件即将销毁时执行
console.log(`按钮 ${this.label} 即将销毁`);
}
// UI构建方法(必须实现)
build() {
Button(this.label)
.type(this.buttonType)
.onClick(() => {
if (this.onClick) {
this.onClick();
}
})
.height(44)
.padding({ left: 16, right: 16 })
}
}
• 必须实现build方法:每个 struct 组件必须包含且仅包含一个 build() 方法,返回唯一的根UI节点
• 禁止继承:struct 不支持继承其他类或组件,仅支持组合复用
• 属性必须初始化:所有非 @Prop/@Link 装饰的属性必须在声明时初始化
• 访问修饰符限制:默认所有属性为public,不支持private/protected修饰符
• 无构造函数:struct 不支持自定义构造函数,实例化通过对象字面量完成
@Component 装饰器用于标记 struct 为UI组件,启用框架的组件生命周期管理和状态监听能力:
• 无参数 @Component:普通组件,可在其他组件中直接引用
• @Entry + @Component:页面入口组件,作为页面根节点,一个页面仅能有一个 @Entry 组件
• @CustomDialog + @Component:自定义弹窗组件,用于弹窗场景
// 页面入口组件示例
@Entry // 标记为页面入口
@Component
struct HomePage {
build() {
Column() {
Text("首页")
.fontSize(24)
// 引用自定义组件
CustomButton({
label: "登录",
buttonType: ButtonType.Primary,
onClick: () => {
console.log("点击登录")
}
})
}
.width('100%')
.height('100%')
.padding(20)
}
}
struct 组件支持以下生命周期钩子方法:
|
生命周期方法 |
调用时机 |
典型应用场景 |
|
aboutToAppear() |
组件创建后,首次build之前调用 |
数据初始化、事件监听注册、资源加载 |
|
aboutToDisappear() |
组件销毁前调用 |
事件监听移除、资源释放、定时器清理 |
|
onPageShow() |
页面显示时调用(仅@Entry组件支持) |
页面恢复时数据刷新、埋点上报 |
|
onPageHide() |
页面隐藏时调用(仅@Entry组件支持) |
页面退后台时状态保存 |
|
onBackPress() |
返回键点击时调用(仅@Entry组件支持) |
拦截返回操作、二次确认弹窗 |
build() 方法是组件UI的描述入口,有严格的执行限制:
• 禁止写入日志(console.log)、复杂计算、网络请求等非UI描述逻辑
• 禁止定义局部变量、函数或类
• 禁止直接调用未被 @Builder 装饰的函数
• 必须返回唯一的根UI节点,多节点需用容器组件(Column/Row/Stack)包裹
• 执行时间应控制在16ms以内,避免影响UI渲染流畅度
性能红线
build() 方法会在状态变化时频繁执行,任何耗时操作都会直接导致UI卡顿,所有非UI逻辑必须移到生命周期方法或事件回调中。
ArkTS 中 struct 与 class 有明确的职责划分,核心差异如下:
|
特性维度 |
struct(UI组件) |
class(逻辑类) |
|
核心用途 |
定义UI组件,描述界面结构与交互 |
定义数据模型、业务逻辑、工具类 |
|
装饰器要求 |
必须配合@Component等UI装饰器使用 |
无强制装饰器要求,可配合@Observed等 |
|
继承支持 |
不支持继承,仅支持组合复用 |
支持类继承、接口实现 |
|
构造函数 |
不支持自定义构造函数 |
支持自定义构造函数 |
|
访问修饰符 |
默认public,不支持private/protected |
支持public/private/protected |
|
build()方法 |
必须实现,用于描述UI结构 |
无此方法 |
|
生命周期 |
支持组件生命周期钩子 |
无内置生命周期 |
|
实例化方式 |
通过对象字面量在build()中引用 |
通过new关键字实例化 |
struct 可与多种装饰器配合实现复杂功能:
/**
* 商品卡片组件
* @description 展示商品信息,支持收藏状态切换
*/
@ObservedV2 // API 12+,启用深度状态监听
@Component
struct ProductCard {
@Prop product: Product; // 商品数据
@Link isFavorite: boolean; // 收藏状态(双向绑定)
@State isLoading: boolean = false; // 加载状态(组件内部状态)
// 监听收藏状态变化
@Watch('isFavorite')
onFavoriteChange(newValue: boolean) {
console.log(`商品${this.product.id}收藏状态变更为: ${newValue}`);
}
// 提取重复UI逻辑
@Builder
PriceTag(price: number) {
Text(`¥${price.toFixed(2)}`)
.fontSize(16)
.fontColor(Color.Red)
.fontWeight(FontWeight.Bold)
}
build() {
Column() {
Image(this.product.imageUrl)
.width('100%')
.height(200)
.objectFit(ImageFit.Cover)
Text(this.product.name)
.fontSize(14)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
this.PriceTag(this.product.price)
Toggle({ type: ToggleType.Checkbox, isOn: this.isFavorite })
.selectedColor(Color.Red)
.onChange((isOn) => {
this.isFavorite = isOn;
})
}
.width('48%')
.padding(10)
.borderRadius(8)
.backgroundColor(Color.White)
}
}
通过以下特性实现组件的高效复用:
• @Builder:提取组件内重复的UI片段,减少代码冗余
• @BuilderParam:支持父组件向子组件传递UI片段,类似Vue的slot、React的render props
• @Styles:提取通用样式集合,实现样式复用
• @Extend:扩展原生组件的样式属性,实现自定义组件样式
// 通用样式定义
@Styles
function cardStyle() {
.borderRadius(8)
.backgroundColor(Color.White)
.shadow({ radius: 4, color: '#1A000000', offsetX: 0, offsetY: 2 })
}
// 扩展Text组件样式
@Extend(Text)
function titleText() {
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
}
// 使用示例
@Component
struct NewsCard {
@Prop title: string;
@Prop content: string;
build() {
Column() {
Text(this.title)
.titleText() // 使用扩展样式
Text(this.content)
.fontSize(14)
.fontColor('#666')
.margin({ top: 8 })
}
.width('100%')
.padding(16)
.cardStyle() // 使用通用样式
}
}
• 单一职责原则:每个组件仅负责一个功能,避免超大组件,组件代码行数建议控制在300行以内
• 分层设计:按基础组件、业务组件、页面组件三层划分,基础组件无业务逻辑,可跨项目复用
• 属性最小化:仅将需要外部传入的属性定义为@Prop/@Link,内部状态使用@State
• 接口化定义:复杂组件属性通过interface定义,提升代码可读性与可维护性
// ✅ 推荐:通过interface定义组件属性
interface UserCardProps {
user: User;
showAvatar?: boolean;
onFollow?: () => void;
onMessage?: () => void;
}
@Component
struct UserCard {
@Prop props: UserCardProps; // 统一接收属性对象
build() {
// 组件实现
}
}
• 状态最小化:仅将需要触发UI更新的数据标记为@State,减少不必要的重渲染
• 避免嵌套过深:组件嵌套层级不超过5层,使用RelativeContainer减少布局嵌套
• 列表性能优化:长列表使用LazyForEach配合reuseId实现组件复用,避免使用ForEach渲染大量数据
• 计算结果缓存:复杂派生状态使用@Computed缓存计算结果,避免build()中重复计算
• 条件渲染优化:频繁切换显示的组件使用Visibility控制,避免if/else反复创建销毁节点
• 命名规范:组件名采用大驼峰命名法,如UserCard、OrderList,文件名与组件名保持一致
• 注释规范:每个组件添加JSDoc注释,说明组件功能、属性含义、使用示例
• 文件组织:组件按功能模块划分,每个组件单独一个.ets文件,配套index.ts导出
• 类型安全:所有属性明确类型,禁止使用any类型,复杂数据结构定义interface
// 组件目录结构示例
// src/main/ets/components/
// ├── common/ # 基础公共组件
// │ ├── CustomButton.ets
// │ ├── Loading.ets
// │ └── index.ts
// ├── business/ # 业务通用组件
// │ ├── UserCard.ets
// │ ├── ProductCard.ets
// │ └── index.ts
// └── pages/ # 页面级组件
// ├── HomePage.ets
// ├── MinePage.ets
// └── index.ts
• 组件预览:所有公共组件添加@Preview装饰器,支持独立预览与调试
• 单元测试:业务逻辑复杂的组件编写单元测试用例,覆盖核心交互场景
• 性能监控:使用DevEco Studio的性能分析工具,监控组件build时间与内存占用
• 多设备适配:通过多设备预览验证组件在不同分辨率下的显示效果
|
问题现象 |
问题原因 |
解决方案 |
|
组件属性修改后UI不更新 |
未使用响应式装饰器,或嵌套对象未配合@Observed |
为状态属性添加@State/@Link/@Prop装饰器,嵌套对象使用@Observed+@ObjectLink |
|
build()方法中调用函数报错 |
build()中调用了未被@Builder装饰的函数 |
将UI生成函数添加@Builder装饰器,或移到build外执行 |
|
组件生命周期未触发 |
组件未被@Component装饰,或生命周期方法名拼写错误 |
检查装饰器是否正确添加,方法名是否为标准生命周期名称 |
|
列表滑动卡顿 |
使用ForEach渲染大量数据,或ListItem未设置reuseId |
替换为LazyForEach,为ListItem添加唯一reuseId |
|
组件内存泄漏 |
aboutToDisappear中未释放事件监听、定时器等资源 |
在aboutToDisappear中清理所有异步资源与事件监听 |
|
struct继承class报错 |
struct不支持继承特性 |
改用组合模式,将逻辑类作为组件属性注入 |
企业级代码审查中,struct组件需满足以下要求:
1. ✅ 组件使用struct定义,配合@Component装饰器,无class定义UI组件的情况
2. ✅ 组件遵循单一职责原则,代码行数不超过300行
3. ✅ 所有属性有明确类型定义,无any类型使用
4. ✅ build()方法中无复杂计算、日志输出、网络请求等非UI逻辑
5. ✅ 所有定时器、事件监听在aboutToDisappear中正确释放
6. ✅ 长列表使用LazyForEach配合reuseId,无ForEach渲染大量数据的情况
7. ✅ 组件有完整的JSDoc注释,说明功能、属性、使用示例
8. ✅ 组件命名符合大驼峰规范,文件名与组件名一致
9. ✅ 公共组件添加@Preview装饰器,支持独立预览
10. ✅ 无struct继承其他类或组件的情况
文档版本:V1.0 | 适配版本:HarmonyOS NEXT API 12+ | 更新日期:2024年4月
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。