系列文章 :鸿蒙NEXT开发实战系列 -- 第3篇(共5篇) 适合人群 :有ArkUI基础,想深入理解状态管理的开发者 开发环境 :DevEco Studio 5.0.5+ | HarmonyOS NEXT (API 14) 阅读时长:约15分钟
上一篇:ArkUI组件库完全指南 | 下一篇:数据持久化与网络请求全攻略
一、引言:为什么状态管理如此重要?
在鸿蒙NEXT应用开发中,状态管理是构建动态UI的核心基石。想象一下,如果一个电商App的商品数量无法实时更新、用户的登录状态无法同步到各个页面、表单输入无法即时反馈------这样的应用体验将是灾难性的。
ArkUI采用声明式UI范式,其核心思想是"状态驱动UI":当状态(State)发生变化时,框架会自动更新对应的UI组件。这种模式相比传统的命令式UI,代码更简洁、逻辑更清晰、维护更方便。
本文将系统讲解ArkUI四大核心状态管理装饰器:@State、@Prop、@Link、@Provide/@Consume,并通过完整的实战代码帮助你彻底掌握它们的使用场景和最佳实践。
二、@State装饰器:组件内的状态管家
2.1 基本概念
@State是最基础的状态装饰器,用于管理组件内部 的状态数据。被@State装饰的变量具有以下特性:
-
响应式:变量值改变时,自动触发UI刷新
-
私有化:仅在当前组件内可见和可修改
-
初始化:支持在组件创建时进行初始化
2.2 基础用法
@Component
struct CounterComponent {
// 使用@State装饰器声明状态变量
@State count: number = 0
build() {
Column() {
Text(`当前计数:${this.count}`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
Row() {
Button('减少')
.onClick(() => {
// 直接修改@State变量,UI会自动更新
this.count--
})
.margin({ right: 10 })
Button('增加')
.onClick(() => {
this.count++
})
.margin({ left: 10 })
}
}
.width('100%')
.padding(20)
.justifyContent(FlexAlign.Center)
}
}
2.3 支持的数据类型
@State支持多种数据类型,但不同类型的行为略有差异:
@Component
struct StateTypesDemo {
// 基础类型:number、string、boolean
@State count: number = 0
@State message: string = 'Hello'
@State isActive: boolean = true
// 对象类型:需要整体赋值才能触发UI更新
@State user: UserInfo = { name: '张三', age: 25 }
// 数组类型:需要整体赋值或使用splice等方法
@State items: string[] = ['苹果', '香蕉', '橙子']
build() {
Column() {
// 基础类型示例
Text(`计数:${this.count}`)
Button('更新计数')
.onClick(() => {
this.count++ // 直接赋值即可
})
// 对象类型示例
Text(`用户名:${this.user.name}`)
Button('更新用户名')
.onClick(() => {
// 错误方式:this.user.name = '李四' // 不会触发UI更新
// 正确方式:整体赋值
this.user = { ...this.user, name: '李四' }
})
// 数组类型示例
ForEach(this.items, (item: string) => {
Text(item)
})
Button('添加水果')
.onClick(() => {
// 错误方式:this.items.push('葡萄') // 不会触发UI更新
// 正确方式:整体赋值
this.items = [...this.items, '葡萄']
})
}
}
}
interface UserInfo {
name: string
age: number
}
2.4 踩坑记录:对象属性修改不触发更新
问题描述:直接修改对象的属性值,UI没有更新。
错误代码:
@State user: UserInfo = { name: '张三', age: 25 }
// 点击后UI没有变化
Button('修改名字')
.onClick(() => {
this.user.name = '李四' // 错误:直接修改属性
})
正确代码:
// 正确方式:整体赋值新对象
Button('修改名字')
.onClick(() => {
this.user = { ...this.user, name: '李四' } // 使用展开运算符创建新对象
})
原理说明 :@State通过引用比较来检测变化,直接修改属性不会改变对象引用,因此框架认为状态没有变化。
三、@Prop装饰器:父子组件的单向数据流
3.1 基本概念
@Prop用于实现父组件到子组件的单向数据传递。特点如下:
-
单向绑定:数据只能从父组件流向子组件
-
本地副本:子组件接收到的是父组件数据的副本,修改不会影响父组件
-
自动同步:父组件数据变化时,子组件自动更新
3.2 基础用法
// 子组件:接收父组件传递的数据
@Component
struct ChildComponent {
@Prop title: string // 使用@Prop接收父组件数据
@Prop count: number
build() {
Column() {
Text(this.title)
.fontSize(20)
.margin({ bottom: 10 })
Text(`子组件计数:${this.count}`)
.fontSize(16)
}
.padding(15)
.backgroundColor('#F5F5F5')
.borderRadius(8)
}
}
// 父组件:传递数据给子组件
@Entry
@Component
struct ParentComponent {
@State parentTitle: string = '来自父组件的标题'
@State parentCount: number = 0
build() {
Column() {
// 父组件的UI
Text(`父组件计数:${this.parentCount}`)
.fontSize(20)
.margin({ bottom: 20 })
Button('父组件更新数据')
.onClick(() => {
this.parentCount++
this.parentTitle = `更新后的标题 ${this.parentCount}`
})
.margin({ bottom: 20 })
// 传递数据给子组件
ChildComponent({
title: this.parentTitle, // 将父组件的title传递给子组件
count: this.parentCount // 将父组件的count传递给子组件
})
}
.width('100%')
.padding(20)
}
}
3.3 @Prop的单向性演示
@Component
struct PropDemoChild {
@Prop message: string
@State localCount: number = 0
build() {
Column() {
Text(`接收到的消息:${this.message}`)
.margin({ bottom: 10 })
Text(`本地计数:${this.localCount}`)
.margin({ bottom: 10 })
Button('子组件修改本地状态')
.onClick(() => {
this.localCount++ // 修改本地状态,子组件UI更新
})
.margin({ bottom: 10 })
Button('尝试修改Prop(无效)')
.onClick(() => {
// this.message = '新消息' // 编译错误:@Prop是只读的
console.info('Prop不能在子组件中修改')
})
}
.padding(15)
.backgroundColor('#E3F2FD')
.borderRadius(8)
}
}
@Entry
@Component
struct PropDemoParent {
@State parentMessage: string = 'Hello from Parent'
build() {
Column() {
Text('父组件')
.fontSize(24)
.margin({ bottom: 20 })
Button('更新父组件消息')
.onClick(() => {
this.parentMessage = `消息更新于 ${new Date().toLocaleTimeString()}`
})
.margin({ bottom: 20 })
PropDemoChild({
message: this.parentMessage
})
}
.width('100%')
.padding(20)
}
}
四、@Link装饰器:父子组件的双向数据绑定
4.1 基本概念
@Link用于实现父子组件之间的双向数据绑定。特点如下:
-
双向绑定:父子组件中任何一方修改数据,另一方都会同步更新
-
同一引用:父子组件共享同一个数据源
-
必须初始化 :父组件必须使用
$$操作符传递数据
4.2 基础用法
// 子组件:使用@Link实现双向绑定
@Component
struct LinkChildComponent {
@Link count: number // 使用@Link装饰器
@Link message: string
build() {
Column() {
Text(`子组件显示:${this.message}`)
.fontSize(18)
.margin({ bottom: 10 })
Text(`计数:${this.count}`)
.fontSize(24)
.margin({ bottom: 15 })
Row() {
Button('子组件 -')
.onClick(() => {
this.count-- // 子组件修改,父组件同步更新
})
.margin({ right: 10 })
Button('子组件 +')
.onClick(() => {
this.count++
})
}
Button('修改消息')
.onClick(() => {
this.message = '子组件修改的消息'
})
.margin({ top: 10 })
}
.padding(15)
.backgroundColor('#E8F5E9')
.borderRadius(8)
}
}
// 父组件
@Entry
@Component
struct LinkParentComponent {
@State parentCount: number = 0
@State parentMessage: string = '来自父组件的消息'
build() {
Column() {
Text('父组件')
.fontSize(24)
.margin({ bottom: 10 })
Text(`父组件显示:${this.parentMessage}`)
.fontSize(18)
.margin({ bottom: 10 })
Text(`父组件计数:${this.parentCount}`)
.fontSize(20)
.margin({ bottom: 15 })
Row() {
Button('父组件 -')
.onClick(() => {
this.parentCount-- // 父组件修改,子组件同步更新
})
.margin({ right: 10 })
Button('父组件 +')
.onClick(() => {
this.parentCount++
})
}
// 使用$$操作符传递数据引用
LinkChildComponent({
count: $parentCount, // $$操作符传递引用
message: $parentMessage
})
}
.width('100%')
.padding(20)
}
}
4.3 @Link与@Prop的对比
// 对比示例:展示@Link和@Prop的区别
// 使用@Prop的子组件
@Component
struct PropChild {
@Prop value: number
build() {
Column() {
Text(`Prop子组件:${this.value}`)
Button('Prop修改')
.onClick(() => {
// this.value = 100 // 编译错误,@Prop是只读的
})
}
.padding(10)
.backgroundColor('#FFEBEE')
}
}
// 使用@Link的子组件
@Component
struct LinkChild {
@Link value: number
build() {
Column() {
Text(`Link子组件:${this.value}`)
Button('Link修改')
.onClick(() => {
this.value = 100 // 可以修改,父组件同步更新
})
}
.padding(10)
.backgroundColor('#E8F5E9')
}
}
@Entry
@Component
struct ComparisonDemo {
@State propValue: number = 0
@State linkValue: number = 0
build() {
Column() {
Text(`Prop父组件:${this.propValue}`)
Text(`Link父组件:${this.linkValue}`)
PropChild({ value: this.propValue })
LinkChild({ value: $linkValue })
}
.width('100%')
.padding(20)
}
}
4.4 踩坑记录:@Link必须使用$$操作符
问题描述:子组件状态没有同步更新。
错误代码:
// 错误:没有使用$$操作符
LinkChildComponent({
count: this.parentCount, // 错误:这会创建副本,不是引用
})
正确代码:
// 正确:使用$$操作符传递引用
LinkChildComponent({
count: $parentCount, // 正确:传递数据引用
})
五、@Provide/@Consume装饰器:跨组件层级的数据共享
5.1 基本概念
@Provide和@Consume用于实现跨组件层级的数据共享,无需通过中间组件逐层传递。特点如下:
-
跨层级传递:可以在任意深度的子组件中访问数据
-
双向绑定 :
@Provide和@Consume之间是双向的 -
一对多 :一个
@Provide可以对应多个@Consume
5.2 基础用法
// 顶层组件:使用@Provide提供数据
@Entry
@Component
struct AppRoot {
@Provide('themeColor') themeColor: string = '#1890FF' // 提供主题颜色
@Provide('language') language: string = 'zh-CN' // 提供语言设置
build() {
Column() {
Text('应用根组件')
.fontSize(24)
.margin({ bottom: 20 })
Row() {
Button('切换主题')
.onClick(() => {
this.themeColor = this.themeColor === '#1890FF' ? '#52C41A' : '#1890FF'
})
.margin({ right: 10 })
Button('切换语言')
.onClick(() => {
this.language = this.language === 'zh-CN' ? 'en-US' : 'zh-CN'
})
}
.margin({ bottom: 30 })
// 中间组件
MiddleComponent()
}
.width('100%')
.padding(20)
}
}
// 中间组件:不处理数据,只负责传递
@Component
struct MiddleComponent {
build() {
Column() {
Text('中间组件(不消费数据)')
.margin({ bottom: 20 })
// 深层子组件
DeepChildComponent()
}
.padding(15)
.backgroundColor('#F0F0F0')
.borderRadius(8)
}
}
// 深层子组件:使用@Consume消费数据
@Component
struct DeepChildComponent {
@Consume('themeColor') themeColor: string // 消费主题颜色
@Consume('language') language: string // 消费语言设置
build() {
Column() {
Text('深层子组件')
.fontSize(18)
.fontColor(this.themeColor) // 使用主题颜色
.margin({ bottom: 10 })
Text(`当前语言:${this.language}`)
.margin({ bottom: 10 })
Text('这是一段示例文本')
.fontColor(this.themeColor)
}
.padding(15)
.border({
width: 2,
color: this.themeColor,
style: BorderStyle.Solid
})
.borderRadius(8)
}
}
5.3 多层级消费示例
// 全局状态管理示例
// 用户信息接口
interface UserInfo {
name: string
isLoggedIn: boolean
}
@Entry
@Component
struct GlobalStateDemo {
@Provide('userInfo') userInfo: UserInfo = {
name: '游客',
isLoggedIn: false
}
@Provide('notification') notification: string = ''
build() {
Column() {
Text('全局状态演示')
.fontSize(24)
.margin({ bottom: 20 })
// 用户状态显示
HeaderComponent()
// 主要内容区
ContentComponent()
// 底部导航
FooterComponent()
}
.width('100%')
.height('100%')
.padding(20)
}
}
// 头部组件
@Component
struct HeaderComponent {
@Consume('userInfo') userInfo: UserInfo
@Consume('notification') notification: string
build() {
Row() {
Text(this.userInfo.isLoggedIn ? `欢迎,${this.userInfo.name}` : '请登录')
.fontSize(18)
if (this.notification) {
Text(this.notification)
.fontSize(14)
.fontColor('#FF4D4F')
.margin({ left: 20 })
}
}
.width('100%')
.padding(10)
.backgroundColor('#E6F7FF')
}
}
// 内容组件
@Component
struct ContentComponent {
build() {
Column() {
Text('主要内容区域')
.margin({ bottom: 20 })
// 登录表单
LoginFormComponent()
}
.flexGrow(1)
.padding(20)
}
}
// 登录表单组件
@Component
struct LoginFormComponent {
@Consume('userInfo') userInfo: UserInfo
@Consume('notification') notification: string
@State username: string = ''
build() {
Column() {
TextInput({ placeholder: '请输入用户名' })
.onChange((value: string) => {
this.username = value
})
.margin({ bottom: 10 })
Button('登录')
.onClick(() => {
if (this.username) {
// 修改全局状态
this.userInfo = {
name: this.username,
isLoggedIn: true
}
this.notification = '登录成功!'
}
})
}
.padding(15)
.backgroundColor('#FFF7E6')
.borderRadius(8)
}
}
// 底部组件
@Component
struct FooterComponent {
@Consume('userInfo') userInfo: UserInfo
build() {
Row() {
Text(this.userInfo.isLoggedIn ? '已登录' : '未登录')
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.padding(10)
.justifyContent(FlexAlign.Center)
}
}
5.4 @Provide/@Consume的双向绑定
@Component
struct BidirectionalDemo {
@Provide('sharedValue') sharedValue: number = 0
build() {
Column() {
Text(`顶层组件值:${this.sharedValue}`)
.fontSize(20)
.margin({ bottom: 20 })
Button('顶层组件 +')
.onClick(() => {
this.sharedValue++
})
.margin({ bottom: 20 })
MiddleLayer()
}
.width('100%')
.padding(20)
}
}
@Component
struct MiddleLayer {
build() {
Column() {
Text('中间层(不消费数据)')
.margin({ bottom: 20 })
BottomLayer()
}
.padding(15)
.backgroundColor('#F5F5F5')
}
}
@Component
struct BottomLayer {
@Consume('sharedValue') sharedValue: number
build() {
Column() {
Text(`底层组件值:${this.sharedValue}`)
.fontSize(18)
.margin({ bottom: 10 })
Button('底层组件 +')
.onClick(() => {
this.sharedValue++ // 修改会同步到顶层组件
})
}
.padding(15)
.backgroundColor('#E8F5E9')
.borderRadius(8)
}
}
@Entry
@Component
struct BidirectionalParent {
build() {
BidirectionalDemo()
}
}
六、状态管理最佳实践与常见陷阱
6.1 最佳实践
1. 合理选择装饰器
// 场景1:组件内部状态 -> 使用@State
@Component
struct LocalStateExample {
@State isVisible: boolean = false // 仅组件内部使用
build() {
Column() {
Button('切换显示')
.onClick(() => {
this.isVisible = !this.isVisible
})
if (this.isVisible) {
Text('现在可见')
}
}
}
}
// 场景2:父子组件单向传递 -> 使用@Prop
@Component
struct ParentComponent {
@State title: string = '标题'
build() {
ChildComponent({ title: this.title })
}
}
@Component
struct ChildComponent {
@Prop title: string // 只读,子组件不需要修改
build() {
Text(this.title)
}
}
// 场景3:父子组件双向绑定 -> 使用@Link
@Component
struct FormParent {
@State inputValue: string = ''
build() {
Column() {
Text(`输入值:${this.inputValue}`)
FormChild({ inputValue: $inputValue })
}
}
}
@Component
struct FormChild {
@Link inputValue: string
build() {
TextInput({ text: this.inputValue })
.onChange((value: string) => {
this.inputValue = value // 双向同步
})
}
}
// 场景4:跨层级共享 -> 使用@Provide/@Consume
@Entry
@Component
struct AppRoot {
@Provide('globalTheme') theme: string = 'light'
build() {
DeepNestedComponent() // 任意深度的子组件都可以访问
}
}
@Component
struct DeepNestedComponent {
@Consume('globalTheme') theme: string
build() {
Text(`当前主题:${this.theme}`)
}
}
2. 避免过度使用@Provide/@Consume
// 不推荐:过度使用@Provide/@Consume
@Entry
@Component
struct BadExample {
@Provide('count') count: number = 0
@Provide('name') name: string = ''
@Provide('age') age: number = 0
// ... 更多Provide
build() {
// 复杂的组件树
}
}
// 推荐:使用状态管理类集中管理
class AppState {
static count: number = 0
static name: string = ''
static age: number = 0
}
// 或者使用AppStorage
@Entry
@Component
struct GoodExample {
@StorageLink('count') count: number = 0
@StorageLink('name') name: string = ''
build() {
// 使用AppStorage管理全局状态
}
}
3. 性能优化:避免不必要的状态更新
@Component
struct OptimizedComponent {
@State data: DataItem[] = []
@State selectedItem: number = -1
build() {
List() {
ForEach(this.data, (item: DataItem, index: number) => {
ListItem() {
Text(item.name)
.backgroundColor(this.selectedItem === index ? '#E6F7FF' : '#FFFFFF')
.onClick(() => {
// 只更新选中状态,不重新渲染整个列表
this.selectedItem = index
})
}
}, (item: DataItem) => item.id.toString()) // 使用唯一key优化
}
}
}
interface DataItem {
id: number
name: string
}
6.2 常见陷阱与解决方案
陷阱1:@State对象属性修改不触发更新
问题代码:
@State user: UserInfo = { name: '张三', age: 25 }
Button('修改')
.onClick(() => {
this.user.name = '李四' // 错误:不会触发UI更新
})
解决方案:
// 方案1:整体赋值
this.user = { ...this.user, name: '李四' }
// 方案2:使用@Observed和@ObjectLink(详见下期文章)
@Observed
class User {
name: string = '张三'
age: number = 25
}
陷阱2:@Link忘记使用$$操作符
问题代码:
// 父组件
@State count: number = 0
ChildComponent({ count: this.count }) // 错误:传递的是值,不是引用
// 子组件
@Link count: number // 子组件修改不会影响父组件
解决方案:
// 父组件
@State count: number = 0
ChildComponent({ count: $count }) // 正确:使用$$操作符传递引用
陷阱3:数组更新不触发UI刷新
问题代码:
@State list: string[] = ['a', 'b', 'c']
Button('添加')
.onClick(() => {
this.list.push('d') // 错误:不会触发UI更新
})
解决方案:
// 方案1:整体赋值
this.list = [...this.list, 'd']
// 方案2:使用splice
this.list.splice(this.list.length, 0, 'd')
this.list = [...this.list] // 触发更新
// 方案3:使用ObservedV2(API 12+)
@ObservedV2
class ListManager {
@Trace list: string[] = ['a', 'b', 'c']
}
陷阱4:@Provide/@Consume key不匹配
问题代码:
// 父组件
@Provide('theme') themeColor: string = 'blue'
// 子组件
@Consume('Theme') themeColor: string // 错误:key不匹配(大小写不同)
解决方案:
// 确保key完全一致
@Provide('theme') themeColor: string = 'blue'
@Consume('theme') themeColor: string // 正确
七、完整实战案例:TodoList应用
// TodoList完整示例
// Todo项接口
interface TodoItem {
id: number
text: string
completed: boolean
}
// 全局状态提供者
@Entry
@Component
struct TodoApp {
@Provide('todos') todos: TodoItem[] = []
@Provide('filter') filter: string = 'all' // all, active, completed
@State nextId: number = 1
// 计算过滤后的列表
getFilteredTodos(): TodoItem[] {
switch (this.filter) {
case 'active':
return this.todos.filter(todo => !todo.completed)
case 'completed':
return this.todos.filter(todo => todo.completed)
default:
return this.todos
}
}
build() {
Column() {
// 标题
Text('TodoList应用')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
// 添加Todo输入框
AddTodoComponent({
onAdd: (text: string) => {
this.todos = [...this.todos, {
id: this.nextId++,
text,
completed: false
}]
}
})
// 过滤器
FilterComponent()
// Todo列表
TodoListComponent({
todos: this.getFilteredTodos()
})
// 统计信息
StatsComponent()
}
.width('100%')
.height('100%')
.padding(20)
}
}
// 添加Todo组件
@Component
struct AddTodoComponent {
@State inputText: string = ''
onAdd: (text: string) => void = () => {}
build() {
Row() {
TextInput({ placeholder: '添加新的待办事项' })
.layoutWeight(1)
.onChange((value: string) => {
this.inputText = value
})
.margin({ right: 10 })
Button('添加')
.onClick(() => {
if (this.inputText.trim()) {
this.onAdd(this.inputText.trim())
this.inputText = ''
}
})
}
.margin({ bottom: 20 })
}
}
// 过滤器组件
@Component
struct FilterComponent {
@Consume('filter') filter: string
build() {
Row() {
Button('全部')
.backgroundColor(this.filter === 'all' ? '#1890FF' : '#F0F0F0')
.fontColor(this.filter === 'all' ? '#FFFFFF' : '#000000')
.onClick(() => {
this.filter = 'all'
})
.margin({ right: 10 })
Button('未完成')
.backgroundColor(this.filter === 'active' ? '#1890FF' : '#F0F0F0')
.fontColor(this.filter === 'active' ? '#FFFFFF' : '#000000')
.onClick(() => {
this.filter = 'active'
})
.margin({ right: 10 })
Button('已完成')
.backgroundColor(this.filter === 'completed' ? '#1890FF' : '#F0F0F0')
.fontColor(this.filter === 'completed' ? '#FFFFFF' : '#000000')
.onClick(() => {
this.filter = 'completed'
})
}
.margin({ bottom: 20 })
}
}
// Todo列表组件
@Component
struct TodoListComponent {
@Prop todos: TodoItem[]
@Consume('todos') allTodos: TodoItem[]
build() {
List() {
ForEach(this.todos, (todo: TodoItem) => {
ListItem() {
TodoItemComponent({
todo,
onToggle: () => {
// 更新todo状态
const index = this.allTodos.findIndex(t => t.id === todo.id)
if (index !== -1) {
const newTodos = [...this.allTodos]
newTodos[index] = { ...newTodos[index], completed: !newTodos[index].completed }
this.allTodos = newTodos
}
},
onDelete: () => {
this.allTodos = this.allTodos.filter(t => t.id !== todo.id)
}
})
}
.margin({ bottom: 10 })
}, (todo: TodoItem) => todo.id.toString())
}
.layoutWeight(1)
}
}
// 单个Todo项组件
@Component
struct TodoItemComponent {
@Prop todo: TodoItem
onToggle: () => void = () => {}
onDelete: () => void = () => {}
build() {
Row() {
// 复选框
Checkbox()
.select(this.todo.completed)
.onChange((value: boolean) => {
this.onToggle()
})
.margin({ right: 10 })
// 文本
Text(this.todo.text)
.fontSize(16)
.decoration({ type: this.todo.completed ? TextDecorationType.LineThrough : TextDecorationType.None })
.layoutWeight(1)
// 删除按钮
Button('删除')
.fontSize(12)
.height(30)
.backgroundColor('#FF4D4F')
.onClick(() => {
this.onDelete()
})
}
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.shadow({ radius: 2, color: '#0000001A', offsetX: 0, offsetY: 2 })
}
}
// 统计组件
@Component
struct StatsComponent {
@Consume('todos') todos: TodoItem[]
get activeCount(): number {
return this.todos.filter(todo => !todo.completed).length
}
get completedCount(): number {
return this.todos.filter(todo => todo.completed).length
}
build() {
Row() {
Text(`总计:${this.todos.length}`)
.margin({ right: 20 })
Text(`未完成:${this.activeCount}`)
.margin({ right: 20 })
Text(`已完成:${this.completedCount}`)
}
.width('100%')
.padding(15)
.justifyContent(FlexAlign.Center)
.backgroundColor('#FAFAFA')
.borderRadius(8)
}
}
八、总结与下期预告
本文要点回顾
-
@State:组件内部状态管理,支持基础类型和引用类型,引用类型需要整体赋值才能触发更新
-
@Prop:父子组件单向数据传递,子组件只能读取不能修改,父组件更新会同步到子组件
-
@Link :父子组件双向数据绑定,必须使用
$$操作符传递引用,任何一方修改都会同步 -
@Provide/@Consume:跨组件层级数据共享,无需逐层传递,支持一对多和双向绑定
装饰器选择指南
| 场景 | 推荐装饰器 | 示例 |
|---|---|---|
| 组件内部状态 | @State | 按钮点击次数、输入框内容 |
| 父传子(只读) | @Prop | 列表项数据、配置信息 |
| 父子双向绑定 | @Link | 表单输入、开关状态 |
| 跨层级共享 | @Provide/@Consume | 主题、语言、用户信息 |
下期预告
第4篇:数据持久化与网络请求全攻略:Preferences、关系数据库、HTTP实战
在下一篇文章中,我们将深入探讨:
-
Preferences轻量级存储:如何保存用户设置、配置信息
-
关系型数据库(RDB):复杂数据的本地存储方案
-
HTTP网络请求:封装通用的网络请求工具类
-
数据缓存策略:提升应用性能的关键技巧
-
完整项目实战:结合状态管理和数据持久化,构建一个完整的新闻阅读应用
九、参考资料
如果这篇文章对你有帮助,请点赞、收藏、关注支持!你的支持是我持续创作的动力!
有问题欢迎在评论区讨论,我会及时回复!
鸿蒙NEXT开发实战系列 -- 全部文章:
-
第3篇:状态管理一文通(当前)
标签:鸿蒙NEXT | HarmonyOS NEXT | ArkUI | ArkTS | DevEco Studio | 状态管理 | @State | @Prop | @Link | @Provide | @Consume | 声明式UI | 数据绑定