概述
在声明式UI编程框架,UI是程序状态的运行结果。
在鸿蒙中,通过对定义的属性(变量)绑定状态变量,当属性(变量)发生变化时,会触发UI的刷新。
基本概念
状态变量:被状态装饰器修饰的变量成为状态变量,当状态变量的值发生变化时候(前提:被鸿蒙刷新框架监听到)会触发UI的渲染更新。
常规变量:没有被状态装饰器装饰的变量。
数据源/同步源:状态变量的原始来源、
从父组件初始化:当父组件向子组件传递了参数,则会覆盖子组件的初始值。
数据传递形式: 只读的单向传递、可变更的双向传递。
框架行为
- 当状态变量被改变时,查询依赖该状态变量的组件;
- 执行依赖该状态变量的组件的更新方法,组件更新渲染;
- 和该状态变量不相关的组件或者UI描述不会发生重新渲染,从而实现页面渲染的按需更新。
V1
@State是最基础的装饰器, 它也是大部分状态变量的数据源。
@State装饰的变量拥有以下特点:
- @State装饰的变量与子组件中的@Prop装饰变量之间建立单向数据同步,与@Link、@ObjectLink装饰变量之间建立双向数据同步。
- @State装饰的变量生命周期与其所属自定义组件的生命周期相同。
注:并不是所有状态变量的更改都会引起UI的刷新,只有被框架观察到的修改才会引起UI的刷新。
- 简单类型(boolean、string、number)
- class或者Object的自身赋值变化和其属性的变化,Object.keys
- 当为arry时, 可以观察到数组本身的赋值和添加、删除、更新数组的变化。
- Date、Map、Set类型可以通过调用相应接口完成值的更新从而被框架监听到。
基本使用
@State是最基础的装饰器,也是大部分状态变量的数据源。
@Prop是用于子组件接收父组件传递来的数据。prop是单向传递的。 prop修饰的数据发生变化时候,只会引起子组件的ui变化。父组件的state修饰的数据发生变化时,会同时引起父子组件的ui刷新。初始化的时候,子组件的初始值会被父组件覆盖。
@Link是用于子组件接收父组件传递来的数据,数据是双向传递的。当子组件数据发生变化的时候,也会让数据源发生变化。会同时会让父子组件的ui刷新。不能赋初始值。
@Provide\ @Consume 是父组件和后代组件进行数据传递的一种方式,和link一样,可以双向传递。
@HMRouter({ pageUrl: 'StateManager' })
@Component
export struct StateManager {
@State state: string = 'state'
@Provide('consumeManager') provideText: string = 'provideText'
build() {
Column() {
Text(this.state)
Button('stateChange')
.onClick(() => {
this.state = 'stateChange'
})
Text(this.provideText)
Button('provideChange')
.onClick(() => {
this.provideText = 'provideChange'
})
LinkManager({ linkText: this.state })
PropManager({ propText: this.state })
}
.width("100%")
.height("100%")
}
}
@Component
export struct PropManager {
@Prop propText: string = 'prop'
build() {
Column() {
Text(this.propText)
Button('propChange')
.onClick(() => {
this.propText = 'propChange'
})
}
.width("100%")
}
}
@Component
export struct LinkManager {
@Link linkText: string
build() {
Column() {
Text(this.linkText)
Button('linkChange')
.onClick(() => {
this.linkText = 'linkChange'
})
ConsumeManager()
}
.width("100%")
}
}
@Component
export struct ConsumeManager {
@Consume('consumeManager') consumeText: string
build() {
Column() {
Text(this.consumeText)
Button('consumeText').onClick(() => {
this.consumeText = 'consumeChange'
})
}
.width("100%")
}
}
注
@Observed装饰的类对象以及使用状态变量装饰器如@State装饰的Class、Date、Map、Set、Array类型的对象添加一层代理用于观测一层属性或API调用产生的变化。这样被装饰Class和Object就变成了Proxy,就不再和之前的Object和Class相同了。
箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。
coverUrl: string = '#00ff00'
changeCoverUrl= (model:PlayDetailViewModel)=> {
model.coverUrl = '#00F5FF'
}
}
@Entry
@Component
struct PlayDetailPage {
@State vm: PlayDetailViewModel = new PlayDetailViewModel();
build() {
Stack() {
Text(this.vm.coverUrl).width(100).height(100).backgroundColor(this.vm.coverUrl)
Row() {
Button('点击改变颜色')
.onClick(() => {
let self = this.vm;
this.vm.changeCoverUrl(self);
})
}
}
.width('100%')
.height('100%')
.alignContent(Alignment.Top)
}
}
开发者可以在aboutToAppear中注册箭头函数,并以此来改变组件中的状态变量。但需要注意的是在aboutToDisappear中将之前注册的函数置空,否则会因为箭头函数捕获了自定义组件的this实例,导致自定义组件无法被释放,从而造成内存泄漏。
关注公众号,查看更多鸿蒙相关技术和代码。