一、前言
在探索 HarmonyOS 的过程中,我们发现了许多有趣且实用的功能和特性。有些总是在不经意间或者触类旁通的找到。或者是某些开发痛点。其中,状态管理是ArkUI开发非常核心的一个东西,我们进行了大量的使用和测试遇到了许多奇奇怪怪的问题。
该系列将着重分享、介绍HarmonyOS API11+的新版本特性或者奇奇怪怪的解决方案、BUG。(弃用API非必要不提及)
二、@State
这应该是用的最多的了,状态变量,将状态变量和UI的某个属性绑定,即可同步两者。
-
初始化行为
组件初始化时,
@State
只能从外面传入一次,之后父组件值的修改并不会影响到组件内部的@State,所以它叫组件内状态~(当然你组件用if、else来更新当我没说)
@Entry
@Component
export struct ExpComponent {
@State title: string = "Title"
build() {
Column() {
ExpChildComponent({
childTitle: this.title
})
Button(this.title)
.onClick(()=>{
this.title = "Click"
})
}
}
}
@Component
export struct ExpChildComponent {
@State childTitle: string = "????"
build() {
Text(this.childTitle)
}
}
-
就像这样,
onClick
触发后ExpChildComponent
并不会发生改变了。 -
太多的@State
- 一开始我们会直接将状态变量直接声明在组件中,如:
@Component
export struct ExpComponent {
@State title: string = ""
build() {
Text(this.title)
}
}
但是随着,一个页面开始变得复杂的时候,我们会面临一堆的Controller,xxState等等,看起来就很头疼。这时候利用@State能观察到Object.keys(自身)
返回的所有属性的特性,我们可以抽出一个ExpUIState
。
@Component
export struct ExpComponent {
@State uiState: ExpUIState = new ExpUIState()
build() {
Text(this.uiState.title)
}
}
class ExpUIState {
title: string = ""
}
-
如此,还你一个优雅的组件。
-
数组
@State能直接修饰数组:
@State title: Model[] = [new Model(1), new Model(2)];
数组自身的赋值可以观察到。
this.title = [new Model(2)];
数组项的赋值可以观察到。
this.title[0] = new Model(2);
删除数组项可以观察到。
this.title.pop();
新增数组项可以观察到。
this.title.push(new Model(12));
数组项中属性的赋值观察不到。
this.title[0].value = 6;
-
数组对象及其子项的赋值是可以被观察到的,所以“titles[0].value = 1 ”这种使用方法是没用滴。
4.作用域的影响 -
得益于闭包的特性(对于Java、Kotlin开发来说不存在的东西)箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。
@Component export struct ExpComponent { @State uiState: ExpUIState = new ExpUIState() build() { Column() { Text(this.uiState.title) Button("Click").onClick(()=>{ this.uiState.autoRefreshTitle() }) } } } class ExpUIState { title: string = "??" autoRefreshTitle = () => { this.title = "AutoRefreshTitle" } }
上述操作,触发
onClick
是不会更新UI的,如果想要触发更新需要用下面的例子:
@Component
export struct ExpComponent {
@State uiState: ExpUIState = new ExpUIState()
aboutToAppear(): void {
}
build() {
Column() {
Text(this.uiState.title)
Button("Click").onClick(()=>{
let fk = this.uiState
this.uiState.autoRefreshTitle(fk)
})
}
}
}
class ExpUIState {
title: string = "??"
autoRefreshTitle = (st:ExpUIState) => {
st.title= "AutoRefreshTitle"
}
}
-
反人类吧?阿弥陀佛。
-
嵌套对象
@State
修饰对象时,里面可能还有对象,或者数组.
class ExpUIState {
childs: ExpChild[] = []
firstChild: ExpChild = new ExpChild()
}
-
很可惜的是,由于
@State
装饰的变量,只能监听到对象本身的地址以及第一层属性的地址变化,也就是firstChild
或者childs
地址的变化,例如如下操作:uiState.firstChild.subTtile=""
,uiState.childs.push(new ExpChild())
等是没法触发刷新的。解决办法就是不用@State
三、@Prop
-
初始化行为
几乎和
@State
一致,但是@Prop
变量允许在本地修改,但修改后的变化不会同步回父组件。当数据源更改时,@Prop
装饰的变量都会更新,并且会覆盖本地所有更改。因此,数值的同步是父组件到子组件(所属组件),子组件数值的变化不会同步到父组件。相比起
@State
重点就是父组件修改后子组件会同步。苦恼@State
没法修改的人,有福了~ -
套一堆的@Prop
不要这么做。如果你的子组件使用
@Prop
、孙子组件也用@Prop
,我其实推荐你使用@Provide
(当然你硬要用也行) -
@Require
使用
@Prop
有一个好处就是,可以使用@Require
。变成一个必传的参数(福音啊)。@State
是可以不传的。
@Require @Prop index: number
Observed
坏消息:@Prop
也没法观察嵌套对象~,因为父组件传入时,用的@State
传入的。好消息:加一个@Observed
就好了~
@Component
export struct ExpComponent {
@State uiState: ExpUIState = new ExpUIState()
aboutToAppear(): void {
}
build() {
Column() {
ExpChildComponent({
child: this.uiState.firstChild
})
Button("Click").onClick(() => {
this.uiState.firstChild.subTitle = "????"
})
}
}
}
@Component
export struct ExpChildComponent {
@Require @Prop child: ExpChild
build() {
Text(this.child.subTitle)
}
}
@Observed
class ExpUIState {
childs: ExpChild[] = []
firstChild: ExpChild = new ExpChild()
}
@Observed
class ExpChild {
subTitle: string = "啊"
}
在嵌套场景下,每一层都要用@Observed装饰,且每一层都要被@Prop接收。
四、@Link
-
初始化行为
@Link
装饰的变量与其父组件中的数据源共享相同的值PS:从API version 9开始,
@Link
子组件从父组件初始化@State
的语法为Comp({ aLink: this.aState })
。同样Comp({aLink: $aState})
也支持(早该如此了) -
给Dialog用
如果你在某个Dialog中,想同步数据到组件,@Link是一个很好的选择:
@CustomDialog
struct CustomDialog01 {
@Link inputValue: string;
controller: CustomDialogController;
build() {
Column() {
Text('Change text')
.fontSize(20)
.margin({ top: 10, bottom: 10 })
TextInput({ placeholder: '', text: this.inputValue })
.height(60)
.width('90%')
.onChange((value: string) => {
this.inputValue = value;
})
}
}
}
@Entry
@Component
struct DialogDemo01 {
@State inputValue: string = 'click me';
dialogController: CustomDialogController = new CustomDialogController({
builder: CustomDialog01({
inputValue: $inputValue
})
})
build() {
Column() {
Button(this.inputValue)
.onClick(() => {
this.dialogController.open();
})
.backgroundColor(0x317aff)
}
.width('100%')
.margin({ top: 5 })
}
}
-
当然,你也可以传个函数进去~
-
@Link套娃
就像
@Prop
一样,我很建议你使用@Provide
、@Consume
五、@Watch
@Watch
应用于对状态变量的监听,这个装饰器可以说是非常直观的一个了,就是用来观察状态变量的。
-
不要在里面修改状态变量
@State @Watch("onUiStateChange") uiState: ExpUIState = new ExpUIState()
onUiStateChange(){
this.uiState.firstChild = new ExpChild()
}
-
相信你看得出来,这是一个死循环吧?
-
子组件事件传递到父组件
可通过状态同步
@Link
或@Provide
和@Consume
进行父子组件间的状态通知,结合@Watch
可以将状态变量的修改在组件间传递,实现类似的功能。 -
粘性
@Watch
没有粘性,所以第一次初始化并不会触发@Watch! -
日志
@Watch可以观察状态变量变化的特性,很适合来做Log日志不是吗~
最后
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。
鸿蒙HarmonyOS Next全套学习资料←点击领取!(安全链接,放心点击)
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
鸿蒙(HarmonyOS NEXT)最新学习路线
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
HarmonyOS Next 最新全套视频教程
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
- ArkTS语言
- 安装DevEco Studio
- 运用你的第一个ArkTS应用
- ArkUI声明式UI开发
- .……
《鸿蒙开发进阶》
- Stage模型入门
- 网络管理
- 数据管理
- 电话服务
- 分布式应用开发
- 通知与窗口管理
- 多媒体技术
- 安全技能
- 任务管理
- WebGL
- 国际化开发
- 应用测试
- DFX面向未来设计
- 鸿蒙系统移植和裁剪定制
- ……
《鸿蒙进阶实战》
- ArkTS实践
- UIAbility应用
- 网络案例
- ……
大厂面试必问面试题
鸿蒙南向开发技术
鸿蒙APP开发必备
鸿蒙生态应用开发白皮书V2.0PDF
总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。