HarmonyOS NEXT应用开发之@State装饰器:组件内状态,2024年最新近期面试题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
img

正文

class ClassA {
public value: string;

constructor(value: string) {
this.value = value;
}
}

class Model {
public value: string;
public name: ClassA;
constructor(value: string, a: ClassA) {
this.value = value;
this.name = a;
}
}

@State装饰的类型是Model

// class类型
@State title: Model = new Model(‘Hello’, new ClassA(‘World’));

对@State装饰变量的赋值。

// class类型赋值
this.title = new Model(‘Hi’, new ClassA(‘ArkUI’));

对@State装饰变量的属性赋值。

// class属性的赋值
this.title.value = ‘Hi’;

嵌套属性的赋值观察不到。

// 嵌套的属性赋值观察不到
this.title.name.value = ‘ArkUI’;

  • 当装饰的对象是array时,可以观察到数组本身的赋值和添加、删除、更新数组的变化。例子如下。
    声明Model类。

class Model {
public value: number;
constructor(value: number) {
this.value = value;
}
}

@State装饰的对象为Model类型数组时。

// 数组类型
@State title: Model[] = [new Model(11), new Model(1)];

数组自身的赋值可以观察到。

// 数组赋值
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;

  • 当装饰的对象是Date时,可以观察到Date整体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的属性。

@Entry
@Component
struct DatePickerExample {
@State selectedDate: Date = new Date(‘2021-08-08’)

build() {
Column() {
Button(‘set selectedDate to 2023-07-08’)
.margin(10)
.onClick(() => {
this.selectedDate = new Date(‘2023-07-08’)
})
Button(‘increase the year by 1’)
.margin(10)
.onClick(() => {
this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1)
})
Button(‘increase the month by 1’)
.margin(10)
.onClick(() => {
this.selectedDate.setMonth(this.selectedDate.getMonth() + 1)
})
Button(‘increase the day by 1’)
.margin(10)
.onClick(() => {
this.selectedDate.setDate(this.selectedDate.getDate() + 1)
})
DatePicker({
start: new Date(‘1970-1-1’),
end: new Date(‘2100-1-1’),
selected: this.selectedDate
})
}.width(‘100%’)
}
}

  • 当装饰的变量是Map时,可以观察到Map整体的赋值,同时可通过调用Map的接口set, clear, delete 更新Map的值。
  • 当装饰的变量是Set时,可以观察到Set整体的赋值,同时可通过调用Set的接口add, clear, delete 更新Set的值。
框架行为
  • 当状态变量被改变时,查询依赖该状态变量的组件;
  • 执行依赖该状态变量的组件的更新方法,组件更新渲染;
  • 和该状态变量不相关的组件或者UI描述不会发生重新渲染,从而实现页面渲染的按需更新。

使用场景

装饰简单类型的变量

以下示例为@State装饰的简单类型,count被@State装饰成为状态变量,count的改变引起Button组件的刷新:

  • 当状态变量count改变时,查询到只有Button组件关联了它;
  • 执行Button组件的更新方法,实现按需刷新。

@Entry
@Component
struct MyComponent {
@State count: number = 0;

build() {
Button(click times: ${this.count})
.onClick(() => {
this.count += 1;
})
}
}

装饰class对象类型的变量
  • 自定义组件MyComponent定义了被@State装饰的状态变量count和title,其中title的类型为自定义类Model。如果count或title的值发生变化,则查询MyComponent中使用该状态变量的UI组件,并进行重新渲染。
  • EntryComponent中有多个MyComponent组件实例,第一个MyComponent内部状态的更改不会影响第二个MyComponent。

class Model {
public value: string;

constructor(value: string) {
this.value = value;
}
}

@Entry
@Component
struct EntryComponent {
build() {
Column() {
// 此处指定的参数都将在初始渲染时覆盖本地定义的默认值,并不是所有的参数都需要从父组件初始化
MyComponent({ count: 1, increaseBy: 2 })
.width(300)
MyComponent({ title: new Model(‘Hello World 2’), count: 7 })
}
}
}

@Component
struct MyComponent {
@State title: Model = new Model(‘Hello World’);
@State count: number = 0;
private increaseBy: number = 1;

build() {
Column() {
Text(${this.title.value})
.margin(10)
Button(Click to change title)
.onClick(() => {
// @State变量的更新将触发上面的Text组件内容更新
this.title.value = this.title.value === ‘Hello ArkUI’ ? ‘Hello World’ : ‘Hello ArkUI’;
})
.width(300)
.margin(10)

Button(Click to increase count = ${this.count})
.onClick(() => {
// @State变量的更新将触发该Button组件的内容更新
this.count += this.increaseBy;
})
.width(300)
.margin(10)
}
}
}

从该示例中,我们可以了解到@State变量首次渲染的初始化流程:

  1. 使用默认的本地初始化:

@State title: Model = new Model(‘Hello World’);
@State count: number = 0;

  1. 对于@State来说,命名参数机制传递的值并不是必选的,如果没有命名参数传值,则使用本地初始化的默认值:

class C1 {
public count:number;
public increaseBy:number;
constructor(count: number, increaseBy:number) {
this.count = count;
this.increaseBy = increaseBy;
}
}
let obj = new C1(1, 2)
MyComponent(obj)

装饰Map类型变量

说明:
从API version 11开始,@State支持Map类型。

在下面的示例中,message类型为Map<number, string>,点击Button改变message的值,视图会随之刷新。

@Entry
@Component
struct MapSample {
@State message: Map<number, string> = new Map([[0, “a”], [1, “b”], [3, “c”]])

build() {
Row() {
Column() {
ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
Text(${item[0]}).fontSize(30)
Text(${item[1]}).fontSize(30)
Divider()
})
Button(‘init map’).onClick(() => {
this.message = new Map([[0, “a”], [1, “b”], [3, “c”]])
})
Button(‘set new one’).onClick(() => {
this.message.set(4, “d”)
})
Button(‘clear’).onClick(() => {
this.message.clear()
})
Button(‘replace the first one’).onClick(() => {
this.message.set(0, “aa”)
})
Button(‘delete the first one’).onClick(() => {
this.message.delete(0)
})
}
.width(‘100%’)
}
.height(‘100%’)
}
}

装饰Set类型变量

说明:
从API version 11开始,@State支持Set类型。

在下面的示例中,message类型为Set,点击Button改变message的值,视图会随之刷新。

@Entry
@Component
struct SetSample {
@State message: Set = new Set([0, 1, 2, 3, 4])

build() {
Row() {
Column() {
ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
Text(${item[0]}).fontSize(30)
Divider()
})
Button(‘init set’).onClick(() => {
this.message = new Set([0, 1, 2, 3, 4])
})
Button(‘set new one’).onClick(() => {
this.message.add(5)
})
Button(‘clear’).onClick(() => {
this.message.clear()
})
Button(‘delete the first one’).onClick(() => {
this.message.delete(0)
})
}
.width(‘100%’)
}
.height(‘100%’)
}
}

State支持联合类型实例

@State支持联合类型和undefined和null,在下面的示例中,count类型为number | undefined,点击Button改变count的属性或者类型,视图会随之刷新。

@Entry
@Component
struct EntryComponent {
build() {
Column() {
MyComponent()
}
}
}

@Component
struct MyComponent {
@State count: number | undefined = 0;

build() {
Column() {
Text(count(${this.count}))
Button(‘change’)
.onClick(() => {
this.count = undefined;
})
}
}
}

常见问题

使用箭头函数改变状态变量未生效

箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。所以在该场景下, changeCoverUrl的this指向PlayDetailViewModel,而不是被装饰器@State代理的状态变量。

反例:

export default class PlayDetailViewModel {
coverUrl: string = ‘#00ff00’

changeCoverUrl= ()=> {
this.coverUrl = ‘#00F5FF’
}

}


import PlayDetailViewModel from ‘./PlayDetailViewModel’

@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(() => {
this.vm.changeCoverUrl()
})
}
}
.width(‘100%’)
.height(‘100%’)
.alignContent(Alignment.Top)
}
}

所以要将当前this.vm传入,调用代理状态变量的属性赋值。

正例:

export default class PlayDetailViewModel {
coverUrl: string = ‘#00ff00’

changeCoverUrl= (model:PlayDetailViewModel)=> {

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

Url()
})
}
}
.width(‘100%’)
.height(‘100%’)
.alignContent(Alignment.Top)
}
}

所以要将当前this.vm传入,调用代理状态变量的属性赋值。

正例:

export default class PlayDetailViewModel {
coverUrl: string = ‘#00ff00’

changeCoverUrl= (model:PlayDetailViewModel)=> {

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-QQG2Jsft-1713133931046)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值