Appearance
ArkTS 装饰器
ArkTS 的核心:装饰器是声明式 UI 的基石,从 @Entry 到 @Builder,每种装饰器都有明确职责。
1. 装饰器概述
装饰器是 ArkTS 特有的语法特性,在编译期向类或成员注入元数据和额外行为。ArkTS 的装饰器分为两大类:
- UI 组件装饰器:定义组件及其状态
- 构建与样式装饰器:定义 UI 复用和样式扩展
装饰器分类总览
UI 组件装饰器
├── @Entry - 页面入口
├── @Component - 自定义组件
├── @CustomDialog - 自定义弹窗
├── @Extend - 组件样式扩展
└── @Styles - 通用样式集合
构建与样式装饰器
├── @Builder - UI 构建函数
├── @BuilderParam - 父组件传递 UI 内容
├── @Styles - 通用样式复用
└── @Extend - 组件特有样式扩展2. @Entry — 页面入口装饰器
2.1 基本用法
typescript
// 必须且只能有一个 @Entry
@Entry
@Component
struct Index {
@State message: string = 'Hello'
build() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.height('100%')
}
}2.2 @Entry 规则
| 规则 | 说明 |
|---|---|
| 数量 | 每个页面文件只能有一个 @Entry |
| 位置 | 必须与 @Component 一起使用 |
| 作用 | 标记该 struct 为页面入口,自动加入页面路由栈 |
| 导航 | @Entry 组件可以使用 Navigation 组件实现子页面导航 |
3. @Component — 自定义组件装饰器
3.1 基本用法
typescript
@Component
struct MyButton {
@State text: string = 'Click Me'
@State bgColor: Color = Color.Blue
build() {
Button(this.text)
.backgroundColor(this.bgColor)
.onClick(() => {
this.bgColor = Color.Red // 触发重新渲染
})
}
}3.2 @Component 的规则
- 必须使用 struct 定义,不能用 class
- 必须有 build() 函数
- 不可继承(与 class 的最大区别)
- 不可用 new 创建(由框架管理实例)
- 可以嵌套使用(组件中嵌套其他组件)
typescript
// 组件嵌套示例
@Component
struct Container {
build() {
Column() {
MyButton() // 子组件直接调用
MyButton()
MyButton()
}
.width('100%')
.height('100%')
}
}4. @Builder — UI 构建函数装饰器
4.1 基本用法:提取可复用 UI 逻辑
typescript
@Component
struct ItemCard {
@State title: string = '标题'
@State content: string = '内容'
// 用 @Builder 提取可复用的 UI 部分
@Builder
renderHeader() {
Row() {
Text(this.title)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Blank() // 填充剩余空间
Text('详情 >')
.fontSize(14)
.fontColor(Color.Gray)
}
.width('100%')
}
build() {
Column() {
this.renderHeader() // 调用 @Builder 函数
Divider()
Text(this.content)
.fontSize(16)
.margin({ left: 10, right: 10, top: 10, bottom: 10 })
}
.width('100%')
.borderRadius(8)
.backgroundColor(Color.White)
}
}4.2 @Builder 的传参方式
typescript
@Component
struct ListCard {
@State items: string[] = ['item1', 'item2', 'item3']
// 按引用传递:UI 随数据刷新
@Builder
renderItemWithRef(item: string) {
Text(item)
.fontSize(16)
}
// 按值传递:UI 不随数据刷新(性能更好,数据不可变)
@Builder
renderItemWithValue(value: string) {
Text(value)
.fontSize(16)
}
build() {
Column() {
// 调用 @Builder 传递数据
ForEach(this.items, (item: string) => {
this.renderItemWithRef(item)
Divider()
})
}
}
}4.3 @Builder 与组件的区别
| 特性 | @Builder | @Component |
|---|---|---|
| 定义方式 | 函数(方法) | struct(结构体) |
| 复用粒度 | UI 逻辑片段 | 完整组件 |
| 参数传递 | 函数参数 | @Prop/@Link |
| 状态管理 | 无状态 | @State 等 |
| 生命周期 | 无 | aboutToAppear 等 |
| 性能 | 更轻量 | 有一定开销 |
💡 面试考点:
@Builder用于提取轻量级 UI 复用逻辑,性能优于创建完整的自定义组件。适用于简单的 UI 片段复用。
5. @BuilderParam — 父组件向子组件传递 UI
5.1 基本用法(类似 slot / render props)
typescript
// 子组件
@Component
struct Dialog {
@BuilderParam contentBuilder: () => void
build() {
Column() {
// 在弹窗内容区渲染父组件传入的 UI
this.contentBuilder()
}
.width('90%')
.borderRadius(12)
.backgroundColor(Color.White)
}
}
// 父组件
@Entry
@Component
struct Index {
build() {
Column() {
Dialog() {
() => { // @BuilderParam 的传参格式
Text('这是弹窗内容')
.fontSize(18)
Button('关闭')
.onClick(() => {
// 关闭弹窗
})
}
}
}
.width('100%')
}
}5.2 带参数的 @BuilderParam
typescript
// 子组件
@Component
struct Card {
@BuilderParam titleBuilder: () => void
@BuilderParam bodyBuilder: (item: string) => void
@State items: string[] = ['数据1', '数据2']
build() {
Column() {
this.titleBuilder()
ForEach(this.items, (item) => {
this.bodyBuilder(item)
})
}
}
}
// 父组件
Card() {
() => Text('卡片标题').fontSize(20)
}
(content: string) => {
Text(content).fontSize(16)
}5.3 @BuilderParam 的应用场景
- 弹窗/Dialog 的内容自定义
- 卡片组件的标题/内容模板
- 表单组件的自定义表单项
- 列表项的自定义布局
💡 面试技巧:说
@BuilderParam时,用类比的方式:"类似 Vue 的 slot(插槽)或 React 的 render props,让父组件向子组件传递一段 UI 结构。"
6. @Styles — 通用样式集合
6.1 基本用法
typescript
// 定义通用样式
@Styles function commonStyle() {
.width('100%')
.height(60)
.borderRadius(8)
.backgroundColor(Color.White)
.padding(12)
}
@Styles function buttonStyle() {
.width(120)
.height(44)
.borderRadius(22)
.fontColor(Color.White)
.fontWeight(FontWeight.Medium)
}
@Entry
@Component
struct Index {
build() {
Column() {
// 应用通用样式
Text('文本')
.commonStyle()
Button('按钮')
.buttonStyle()
.backgroundColor(Color.Blue)
}
}
}6.2 @Styles 的限制
- 只能使用通用属性(width/height/borderRadius 等),不能用组件特有属性
- 不支持参数
- 不支持多态(不同组件应用同一 @Styles 效果相同)
7. @Extend — 组件特有样式扩展
7.1 基本用法
typescript
// 针对特定组件(如 Text)的样式扩展
@Extend(Text) function textStyle(fontSize: number, color: Color = Color.Black) {
.fontSize(fontSize)
.fontColor(color)
.fontWeight(FontWeight.Normal)
}
// 针对特定组件(如 Button)的样式扩展
@Extend(Button) function myButtonStyle() {
.width('100%')
.height(48)
.borderRadius(24)
.backgroundColor(Color.Blue)
.fontColor(Color.White)
.fontWeight(FontWeight.Medium)
}
@Entry
@Component
struct Index {
build() {
Column() {
Text('大标题')
.textStyle(28, Color.Blue)
Text('小标题')
.textStyle(16, Color.Gray)
Button('点击')
.myButtonStyle()
}
}
}7.2 @Styles vs @Extend 对比
| 特性 | @Styles | @Extend |
|---|---|---|
| 作用范围 | 通用属性(多组件) | 组件特有属性(特定组件) |
| 支持参数 | ❌ 不支持 | ✅ 支持 |
| 多态 | ✅ 可应用到不同组件 | ❌ 仅限指定组件 |
| 示例 | width/height/borderRadius | fontSize/fontColor(仅 Text) |
💡 记忆口诀:
- @Styles = 通用属性集合(.width, .height),多态
- @Extend = 针对特定组件的私有属性扩展(如 Text 的 .fontColor),支持参数
8. @CustomDialog — 自定义弹窗装饰器
8.1 基本用法
typescript
@CustomDialog
struct MyDialog {
controller: CustomDialogController
@State title: string = '提示'
@State content: string = '确定要删除吗?'
build() {
Column() {
Text(this.title)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 16 })
Text(this.content)
.fontSize(16)
.margin({ bottom: 24 })
Row() {
Button('取消')
.width('48%')
.onClick(() => {
this.controller.close()
})
Button('确定')
.width('48%')
.onClick(() => {
// 执行删除
this.controller.close()
})
}
.width('100%')
}
.width('80%')
.padding(24)
}
}
@Entry
@Component
struct Index {
private dialogController: CustomDialogController = new CustomDialogController({
builder: MyDialog({ controller: this.dialogController }),
customPanelState: CustomPanelState.POPUP,
alignment: DialogAlignment.Center,
offset: { dx: 0, dy: 0 }
})
build() {
Column() {
Button('打开弹窗')
.onClick(() => {
this.dialogController.open()
})
}
}
}9. 装饰器完整速查表
| 装饰器 | 作用 | 用在 | 是否必选 |
|---|---|---|---|
@Entry | 页面入口 | struct | 每个页面一个 |
@Component | 自定义组件 | struct | 自定义组件必选 |
@State | 本地状态 | 属性 | 按需 |
@Prop | 单向数据流 | 属性 | 按需 |
@Link | 双向数据流 | 属性 | 按需 |
@Builder | UI 构建函数 | 方法 | 按需 |
@BuilderParam | 传递 UI 内容 | 属性 | 按需 |
@Styles | 通用样式 | 函数 | 按需 |
@Extend | 组件特有样式 | 函数 | 按需 |
@Observed | 深度监听 | class | 按需 |
@Trace | V2 深度监听 | 属性 | 按需 |
10. 面试高频考点
Q1: @Builder 和 @BuilderParam 的区别?
回答:
@Builder:提取组件内部的轻量级 UI 逻辑复用@BuilderParam:让父组件向子组件传递一段 UI 结构(类似 slot/render props)
Q2: @Styles 和 @Extend 的区别?
回答:
@Styles:通用属性集合(如 .width/.height),支持多组件复用@Extend:针对特定组件(如 Text/Button)的私有属性扩展,支持参数
Q3: @Component 定义的组件可以继承吗?
回答:不可以。ArkTS 的 @Component struct 不支持继承,这是与 class 的最大区别。
🐱 小猫提示:装饰器是 ArkTS 面试的核心考点,尤其是 @Builder/@BuilderParam、@Styles/@Extend 这两对的区别。记住类比记忆法就稳了!