文章目录
一、@State装饰器:组件内状态
@State
装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。- 当状态改变时,UI会发生对应的渲染改变。
- 嵌套类型以及数组中的对象属性无法触发视图更新。
- 允许装饰的变量类型:
Object、class、string、number、boolean、enum
类型,以及这些类型的数组
。 - 必须本地初始化,不能为空值。
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.message = 'hello ArkTs!'
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
二、@Prop装饰器:父子单向同步
-
@Prop
装饰的变量可以和父组件建立单向的同步关系。 -
单向同步:对父组件状态变量值的修改,将同步给子组件
@Prop
装饰的变量,子组件@Prop
变量的修改不会同步到父组件的状态变量上。 -
@Prop
装饰器不能在@Entry
装饰的自定义组件中使用。 -
允许装饰的变量类型:
string、number、boolean、enum
类型。 -
不可以是数组、any
-
父组件是对象类型时,子组件是对象属性,如下代码所示:
// 任务统计信息
class StatInfo {
totalTask: number = 0
finishTask: number = 0
}
// 统一的卡片样式
@Styles function card() {
.width('95%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(15)
.shadow({ radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4 })
}
@Entry
@Component
struct PropPage {
// 统计信息
@State stat: StatInfo = new StatInfo()
// 父组件数剧是简单类型时
// @State totalTask: number = 0
// @State finishTask: number = 0
build() {
Column({ space: 10 }) {
// 调用子组件并传参
TaskStatistics({ totalTask: this.stat.totalTask, finishTask: this.stat.finishTask })
// 父组件数剧是简单类型时
// TaskStatistics({ totalTask: this.totalTask, finishTask: this.finishTask })
}
.width('100%')
.height('100%')
.backgroundColor('#F1F2F3')
}
}
// 子组件
@Component
struct TaskStatistics {
@Prop totalTask: number
@Prop finishTask: number
build() {
Stack() {
Progress({
value: this.finishTask,
total: this.totalTask,
type: ProgressType.Ring
})
.width(100)
Row() {
Text(this.finishTask.toString())
.fontSize(24)
.fontColor('#36D')
Text(' / ' + this.totalTask.toString())
.fontSize(24)
}
}
.card()
.margin({ top: 5, bottom: 10 })
}
}
三、@Link装饰器:父子双向同步
- 子组件中被
@Link
装饰的变量与其父组件中对应的数据源建立双向数据绑定。 - 双向同步:对父组件状态变量值的修改,将同步给子组件
@Prop
装饰的变量,反之亦然。 @Link
装饰器不能在@Entry
装饰的自定义组件中使用。- 嵌套类型以及数组中的对象属性无法触发视图更新。
- 子组件从父组件初始化
@State
的语法为子组件名({ aLink: $aState })
- 允许装饰的变量类型:
Object、class、string、number、boolean、enum
类型,以及这些类型的数组。如下代码所示:
// 任务统计信息
class StatInfo {
totalTask: number = 0
finishTask: number = 0
}
// 任务类
class Task {
static id: number = 1
// 任务名称
name: string = `任务${Task.id++}`
// 任务状态:是否完成
finished: boolean = false
}
@Entry
@Component
struct PropPage {
// 统计信息
@State stat: StatInfo = new StatInfo()
// 父组件数剧是简单类型时
// @State totalTask: number = 0
// @State finishTask: number = 0
build() {
Column({ space: 10 }) {
// 调用子组件并传参
TaskList({ stat: $stat })
// 父组件数剧是简单类型时
// TaskList({totalTask: $totalTask, finishTask: $finishTask })
}
.width('100%')
.height('100%')
.backgroundColor('#F1F2F3')
}
}
@Component
struct TaskList {
// 总任务数量
@Link stat: StatInfo
// 任务数组
@State tasks: Task[] = []
handleTaskChange() {
// 1.更新任务总数量
this.stat.totalTask = this.tasks.length
// 2.更新已完成任务数量
this.stat.finishTask = this.tasks.filter(item => item.finished).length
}
// 父组件数剧是简单类型时
// @Link totalTask: number
// @Link finishTask: number
// handleTaskChange() {
// // 1.更新任务总数量
// this.totalTask = this.tasks.length
// // 2.更新已完成任务数量
// this.finishTask = this.tasks.filter(item => item.finished).length
// }
build() {
Column() {
Button('新增任务')
.width(200)
.margin({ bottom: 10 })
.onClick(() => {
// 1.新增任务数据
this.tasks.push(new Task())
// 2.更新任务总数量
this.handleTaskChange()
})
}
}
}
四、@Provide和@Consume:与后代组件双向同步
@Provide
和@Consume
,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。- 不同于上文提到的父子组件之间通过命名参数机制传递,
@Provide
和@Consume
摆脱参数传递机制的束缚,实现跨层级传递。格式为子组件名()
@Provide
装饰的变量是在祖先节点中,可以理解为被“提供”给后代的状态变量。@Consume
装饰的变量是在后代组件中,去“消费(绑定)”祖先节点提供的变量。- 示例代码如下:
// 任务统计信息
class StatInfo {
totalTask: number = 0
finishTask: number = 0
}
@Entry
@Component
struct PropPage {
// 统计信息
@Provide stat: StatInfo = new StatInfo()
build() {
Column({ space: 10 }) {
// 调用子组件
TaskStatistics()
}
.width('100%')
.height('100%')
.backgroundColor('#F1F2F3')
}
}
@Component
struct TaskStatistics {
@Consume stat: StatInfo
build() {
Stack() {
Progress({
value: this.stat.finishTask,
total: this.stat.totalTask,
type: ProgressType.Ring
})
.width(100)
Row() {
Text(this.stat.finishTask.toString())
.fontSize(24)
.fontColor('#36D')
Text(' / ' + this.stat.totalTask.toString())
.fontSize(24)
}
}
.card()
.margin({ top: 5, bottom: 10 })
}
}
五、@Observed和@ObjectLink:嵌套类对象属性变化
@ObjectLink
和@Observed
类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步- 被
@Observed
装饰的类,可以被观察到属性的变化 - 单独使用
@Observed
是没有任何作用的,需要搭配@ObjectLink
或者@Prop
使用。 @ObjectLink
装饰器不能在@Entry
装饰的自定义组件中使用。
// 任务类
@Observed
class Task {
static id: number = 1
// 任务名称
name: string = `任务${Task.id++}`
// 任务状态:是否完成
finished: boolean = false
}
// 任务完成样式
@Extend(Text) function finishedTask() {
.decoration({ type: TextDecorationType.LineThrough })
.fontColor('#B1B2B1')
}
@Component
struct TaskList {
// 总任务数量
@Consume stat: StatInfo
// 任务数组
@State tasks: Task[] = []
handleTaskChange() {
// 1.更新任务总数量
this.stat.totalTask = this.tasks.length
// 2.更新已完成任务数量
this.stat.finishTask = this.tasks.filter(item => item.finished).length
}
build() {
Column() {
// 新增任务按钮
Button('新增任务')
.width(200)
.margin({ bottom: 10 })
.onClick(() => {
// 1.新增任务数据
this.tasks.push(new Task())
// 2.更新任务总数量
this.handleTaskChange()
})
// 任务列表
List({ space: 10 }) {
ForEach(
this.tasks,
(item: Task, index) => {
ListItem() {
TaskItem({ item: item, onTaskChange: this.handleTaskChange.bind(this) })
}
.swipeAction({ end: this.DeleteButton(index) })
}
)
}
.width('100%')
.layoutWeight(1)
.alignListItem(ListItemAlign.Center)
}
}
@Builder DeleteButton(index: number) {
Button() {
Image($r('app.media.img_user_avatar'))
.fillColor(Color.White)
.width(20)
}
.width(40)
.height(40)
.type(ButtonType.Circle)
.backgroundColor(Color.Red)
.margin(5)
.onClick(() => {
this.tasks.splice(index, 1)
this.handleTaskChange()
})
}
}
@Component
struct TaskItem {
@ObjectLink item: Task
onTaskChange: () => void
build() {
Row() {
if (this.item.finished) {
Text(this.item.name)
.finishedTask()
} else {
Text(this.item.name)
}
Checkbox()
.select(this.item.finished)
.onChange(val => {
// 1.更新当前任务状态
this.item.finished = val
// 2.更新已完成任务数量
this.onTaskChange()
})
}
.card()
.justifyContent(FlexAlign.SpaceBetween)
}
}