ToB企服应用市场:ToB评测及商务社交产业平台
标题:
鸿蒙Harmony应用开发—ArkTS(@Prop装饰器:父子单向同步)
[打印本页]
作者:
篮之新喜
时间:
2024-8-8 00:29
标题:
鸿蒙Harmony应用开发—ArkTS(@Prop装饰器:父子单向同步)
@Prop装饰的变量可以和父组件创建单向的同步关系。@Prop装饰的变量是可变的,但是厘革不会同步回其父组件。
分析:
从API version 9开始,该装饰器支持在ArkTS卡片中使用。
概述
@Prop装饰的变量和父组件创建单向的同步关系:
@Prop变量答应在本地修改,但修改后的厘革不会同步回父组件。
当数据源更改时,@Prop装饰的变量都会更新,而且会覆盖本地全部更改。因此,数值的同步是父组件到子组件(所属组件),子组件数值的厘革不会同步到父组件。
限制条件
@Prop装饰变量时会举行深拷贝,在拷贝的过程中除了根本类型、Map、Set、Date、Array外,都会丢失类型。例如PixelMap等通过NAPI提供的复杂类型,由于有部分实现在Native侧,因此无法在ArkTS侧通过深拷贝获得完整的数据。
@Prop装饰器不能在@Entry装饰的自定义组件中使用。
装饰器使用规则分析
@Prop变量装饰器分析装饰器参数无同步类型单向同步:对父组件状态变量值的修改,将同步给子组件@Prop装饰的变量,子组件@Prop变量的修改不会同步到父组件的状态变量上。嵌套类型的场景请参考观察厘革。答应装饰的变量类型Object、class、string、number、boolean、enum类型,以及这些类型的数组。
不支持any,支持undefined和null。
支持Date类型。
API11及以上支持Map、Set类型。
支持类型的场景请参考观察厘革。
API11及以上支持上述支持类型的团结类型,比如string | number, string | undefined 或者 ClassA | null,示例见Prop支持团结类型实例。
留意
当使用undefined和null的时间,建议显式指定类型,遵循TypeScipt类型校验,比如:@Prop a : string | undefined = undefined是推荐的,不推荐@Prop a: string = undefined。支持AkrUI框架定义的团结类型Length、ResourceStr、ResourceColor类型。必须指定类型。
@Prop和数据源类型必要雷同,有以下三种情况:
- @Prop装饰的变量和@State以及其他装饰器同步时双方的类型必须雷同,示例请参考父组件@State到子组件@Prop简单数据类型同步。
- @Prop装饰的变量和@State以及其他装饰器装饰的数组的项同步时 ,@Prop的类型必要和@State装饰的数组的数组项雷同,比如@Prop : T和@State : Array<T>,示例请参考父组件@State数组中的项到子组件@Prop简单数据类型同步。
- 当父组件状态变量为Object或者class时,@Prop装饰的变量和父组件状态变量的属性类型雷同,示例请参考从父组件中的@State类对象属性到@Prop简单类型的同步。嵌套传递层数在组件复用场景,建议@Prop深度嵌套数据不要凌驾5层,嵌套太多会导致深拷贝占用的空间过大以及GarbageCollection(垃圾接纳),引起性能题目,此时更建议使用@ObjectLink。被装饰变量的初始值答应本地初始化。如果在API 11中和@Require团结使用,则必须父组件构造传参。
变量的传递/访问规则分析
传递/访问分析从父组件初始化如果本地有初始化,则是可选的。没有的话,则必选,支持父组件中的常规变量(常规变量对@Prop赋值,只是数值的初始化,常规变量的厘革不会触发UI刷新。只有状态变量才能触发UI刷新)、@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink和@LocalStorageProp去初始化子组件中的@Prop变量。用于初始化子组件@Prop支持去初始化子组件中的常规变量、@State、@Link、@Prop、@Provide。是否支持组件外访问@Prop装饰的变量是私有的,只能在组件内访问。
图1
初始化规则图示
观察厘革和举动表现
观察厘革
@Prop装饰的数据可以观察到以下厘革。
当装饰的类型是答应的类型,即Object、class、string、number、boolean、enum类型都可以观察到赋值的厘革。
// 简单类型
@Prop count: number;
// 赋值的变化可以被观察到
this.count = 1;
// 复杂类型
@Prop title: Model;
// 可以观察到赋值的变化
this.title = new Model('Hi');
复制代码
当装饰的类型是Object或者class复杂类型时,可以观察到第一层的属性的厘革,属性即Object.keys(observedObject)返回的全部属性;
class ClassA {
public value: string;
constructor(value: string) {
this.value = value;
}
}
class Model {
public value: string;
public a: ClassA;
constructor(value: string, a: ClassA) {
this.value = value;
this.a = a;
}
}
@Prop title: Model;
// 可以观察到第一层的变化
this.title.value = 'Hi'
// 观察不到第二层的变化
this.title.a.value = 'ArkUi'
复制代码
对于嵌套场景,如果class是被@Observed装饰的,可以观察到class属性的厘革,示例请参考@Prop嵌套场景。
当装饰的类型是数组的时间,可以观察到数组本身的赋值和数组项的添加、删除和更新。
// @State装饰的对象为数组时
@Prop title: string[]
// 数组自身的赋值可以观察到
this.title = ['1']
// 数组项的赋值可以观察到
this.title[0] = '2'
// 删除数组项可以观察到
this.title.pop()
// 新增数组项可以观察到
this.title.push('3')
复制代码
对于@State和@Prop的同步场景:
使用父组件中@State变量的值初始化子组件中的@Prop变量。当@State变量厘革时,该变量值也会同步更新至@Prop变量。
@Prop装饰的变量的修改不会影响其数据源@State装饰变量的值。
除了@State,数据源也可以用@Link或@Prop装饰,对@Prop的同步机制是雷同的。
数据源和@Prop变量的类型必要雷同,@Prop答应简单类型和class类型。
当装饰的对象是Date时,可以观察到Date团体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的属性。
@Component
struct DateComponent {
@Prop selectedDate: Date = new Date('');
build() {
Column() {
Button('child update the new date')
.margin(10)
.onClick(() => {
this.selectedDate = new Date('2023-09-09')
})
Button(`child increase the year by 1`).onClick(() => {
this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1)
})
DatePicker({
start: new Date('1970-1-1'),
end: new Date('2100-1-1'),
selected: this.selectedDate
})
}
}
}
@Entry
@Component
struct ParentComponent {
@State parentSelectedDate: Date = new Date('2021-08-08');
build() {
Column() {
Button('parent update the new date')
.margin(10)
.onClick(() => {
this.parentSelectedDate = new Date('2023-07-07')
})
Button('parent increase the day by 1')
.margin(10)
.onClick(() => {
this.parentSelectedDate.setDate(this.parentSelectedDate.getDate() + 1)
})
DatePicker({
start: new Date('1970-1-1'),
end: new Date('2100-1-1'),
selected: this.parentSelectedDate
})
DateComponent({ selectedDate: this.parentSelectedDate })
}
}
}
复制代码
当装饰的变量是Map时,可以观察到Map团体的赋值,同时可通过调用Map的接口set, clear, delete 更新Map的值。详见装饰Map类型变量。
当装饰的变量是Set时,可以观察到Set团体的赋值,同时可通过调用Set的接口add, clear, delete 更新Set的值。详见装饰Set类型变量。
框架举动
要理解@Prop变量值初始化和更新机制,有须要了解父组件和拥有@Prop变量的子组件初始渲染和更新流程。
初始渲染:
实行父组件的build()函数将创建子组件的新实例,将数据源传递给子组件;
初始化子组件@Prop装饰的变量。
更新:
子组件@Prop更新时,更新仅停留在当前子组件,不会同步回父组件;
当父组件的数据源更新时,子组件的@Prop装饰的变量将被来自父组件的数据源重置,全部@Prop装饰的本地的修改将被父组件的更新覆盖。
分析:
@Prop装饰的数据更新依靠其所属自定义组件的重新渲染,所以在应用进入背景后,@Prop无法刷新,推荐使用@Link代替。
使用场景
父组件@State到子组件@Prop简单数据类型同步
以下示例是@State到子组件@Prop简单数据同步,父组件ParentComponent的状态变量countDownStartValue初始化子组件CountDownComponent中@Prop装饰的count,点击“Try again”,count的修改仅保留在CountDownComponent 不会同步给父组件ParentComponent。
ParentComponent的状态变量countDownStartValue的厘革将重置CountDownComponent的count。
@Component
struct CountDownComponent {
@Prop count: number = 0;
costOfOneAttempt: number = 1;
build() {
Column() {
if (this.count > 0) {
Text(`You have ${this.count} Nuggets left`)
} else {
Text('Game over!')
}
// @Prop装饰的变量不会同步给父组件
Button(`Try again`).onClick(() => {
this.count -= this.costOfOneAttempt;
})
}
}
}
@Entry
@Component
struct ParentComponent {
@State countDownStartValue: number = 10;
build() {
Column() {
Text(`Grant ${this.countDownStartValue} nuggets to play.`)
// 父组件的数据源的修改会同步给子组件
Button(`+1 - Nuggets in New Game`).onClick(() => {
this.countDownStartValue += 1;
})
// 父组件的修改会同步给子组件
Button(`-1 - Nuggets in New Game`).onClick(() => {
this.countDownStartValue -= 1;
})
CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
}
}
}
复制代码
在上面的示例中:
CountDownComponent子组件初次创建时其@Prop装饰的count变量将从父组件@State装饰的countDownStartValue变量初始化;
按“+1”或“-1”按钮时,父组件的@State装饰的countDownStartValue值会厘革,这将触发父组件重新渲染,在父组件重新渲染过程中会刷新使用countDownStartValue状态变量的UI组件并单向同步更新CountDownComponent子组件中的count值;
更新count状态变量值也会触发CountDownComponent的重新渲染,在重新渲染过程中,评估使用count状态变量的if语句条件(this.count > 0),并实行true分支中的使用count状态变量的UI组件相关描述来更新Text组件的UI表现;
当按下子组件CountDownComponent的“Try again”按钮时,其@Prop变量count将被更改,但是count值的更改不会影响父组件的countDownStartValue值;
父组件的countDownStartValue值会厘革时,父组件的修改将覆盖掉子组件CountDownComponent中count本地的修改。
父组件@State数组项到子组件@Prop简单数据类型同步
父组件中@State如果装饰的数组,其数组项也可以初始化@Prop。以下示例中父组件Index中@State装饰的数组arr,将其数组项初始化子组件Child中@Prop装饰的value。
@Component
struct Child {
@Prop value: number = 0;
build() {
Text(`${this.value}`)
.fontSize(50)
.onClick(() => {
this.value++
})
}
}
@Entry
@Component
struct Index {
@State arr: number[] = [1, 2, 3];
build() {
Row() {
Column() {
Child({ value: this.arr[0] })
Child({ value: this.arr[1] })
Child({ value: this.arr[2] })
Divider().height(5)
ForEach(this.arr,
(item: number) => {
Child({ value: item })
},
(item: string) => item.toString()
)
Text('replace entire arr')
.fontSize(50)
.onClick(() => {
// 两个数组都包含项“3”。
this.arr = this.arr[0] == 1 ? [3, 4, 5] : [1, 2, 3];
})
}
}
}
}
复制代码
初始渲染创建6个子组件实例,每个@Prop装饰的变量初始化都在本地拷贝了一份数组项。子组件onclick事件处置处罚程序会更改局部变量值。
如果点击界面上的“1”六下,“2”五下、“3”四下,将全部变量的本地取值都变为“7”。
7
7
7
----
7
7
7
复制代码
单击replace entire arr后,屏幕将表现以下信息。
3
4
5
----
7
4
5
复制代码
在子组件Child中做的全部的修改都不会同步回父组件Index组件,所以纵然6个组件表现都为7,但在父组件Index中,this.arr生存的值依旧是[1,2,3]。
点击replace entire arr,this.arr[0] == 1创建,将this.arr赋值为[3, 4, 5];
因为this.arr[0]已更改,Child({value: this.arr[0]})组件将this.arr[0]更新同步到实例@Prop装饰的变量。Child({value: this.arr[1]})和Child({value: this.arr[2]})的情况也类似。
this.arr的更改触发ForEach更新,this.arr更新的前后都有数值为3的数组项:[3, 4, 5] 和[1, 2, 3]。根据diff算法,数组项“3”将被保留,删除“1”和“2”的数组项,添加为“4”和“5”的数组项。这就意味着,数组项“3”的组件不会重新天生,而是将其移动到第一位。所以“3”对应的组件不会更新,此时“3”对应的组件数值为“7”,ForEach最终的渲染结果是“7”,“4”,“5”。
从父组件中的@State类对象属性到@Prop简单类型的同步
如果图书馆有一本图书和两位用户,每位用户都可以将图书标记为已读,此标记举动不会影响别的读者用户。从代码角度讲,对@Prop图书对象的本地更改不会同步给图书馆组件中的@State图书对象。
在此示例中,图书类可以使用@Observed装饰器,但不是必须的,只有在嵌套布局时必要此装饰器。这一点我们会在从父组件中的@State数组项到@Prop class类型的同步分析。
class Book {
public title: string;
public pages: number;
public readIt: boolean = false;
constructor(title: string, pages: number) {
this.title = title;
this.pages = pages;
}
}
@Component
struct ReaderComp {
@Prop book: Book = new Book("", 0);
build() {
Row() {
Text(this.book.title)
Text(`...has${this.book.pages} pages!`)
Text(`...${this.book.readIt ? "I have read" : 'I have not read it'}`)
.onClick(() => this.book.readIt = true)
}
}
}
@Entry
@Component
struct Library {
@State book: Book = new Book('100 secrets of C++', 765);
build() {
Column() {
ReaderComp({ book: this.book })
ReaderComp({ book: this.book })
}
}
}
复制代码
从父组件中的@State数组项到@Prop class类型的同步
在下面的示例中,更改了@State 装饰的allBooks数组中Book对象上的属性,但点击“Mark read for everyone”无反应。这是因为该属性是第二层的嵌套属性,@State装饰器只能观察到第一层属性,不会观察到此属性更改,所以框架不会更新ReaderComp。
let nextId: number = 1;
// @Observed
class Book {
public id: number;
public title: string;
public pages: number;
public readIt: boolean = false;
constructor(title: string, pages: number) {
this.id = nextId++;
this.title = title;
this.pages = pages;
}
}
@Component
struct ReaderComp {
@Prop book: Book = new Book("", 1);
build() {
Row() {
Text(` ${this.book ? this.book.title : "Book is undefined"}`).fontColor('#e6000000')
Text(` has ${this.book ? this.book.pages : "Book is undefined"} pages!`).fontColor('#e6000000')
Text(` ${this.book ? this.book.readIt ? "I have read" : 'I have not read it' : "Book is undefined"}`).fontColor('#e6000000')
.onClick(() => this.book.readIt = true)
}
}
}
@Entry
@Component
struct Library {
@State allBooks: Book[] = [new Book("C#", 765), new Book("JS", 652), new Book("TS", 765)];
build() {
Column() {
Text('library`s all time favorite')
.width(312)
.height(40)
.backgroundColor('#0d000000')
.borderRadius(20)
.margin(12)
.padding({ left: 20 })
.fontColor('#e6000000')
ReaderComp({ book: this.allBooks[2] })
.backgroundColor('#0d000000')
.width(312)
.height(40)
.padding({ left: 20, top: 10 })
.borderRadius(20)
.colorBlend('#e6000000')
Divider()
Text('Books on loaan to a reader')
.width(312)
.height(40)
.backgroundColor('#0d000000')
.borderRadius(20)
.margin(12)
.padding({ left: 20 })
.fontColor('#e6000000')
ForEach(this.allBooks, (book: Book) => {
ReaderComp({ book: book })
.margin(12)
.width(312)
.height(40)
.padding({ left: 20, top: 10 })
.backgroundColor('#0d000000')
.borderRadius(20)
},
(book: Book) => book.id.toString())
Button('Add new')
.width(312)
.height(40)
.margin(12)
.fontColor('#FFFFFF 90%')
.onClick(() => {
this.allBooks.push(new Book("JA", 512));
})
Button('Remove first book')
.width(312)
.height(40)
.margin(12)
.fontColor('#FFFFFF 90%')
.onClick(() => {
if (this.allBooks.length > 0){
this.allBooks.shift();
} else {
console.log("length <= 0")
}
})
Button("Mark read for everyone")
.width(312)
.height(40)
.margin(12)
.fontColor('#FFFFFF 90%')
.onClick(() => {
this.allBooks.forEach((book) => book.readIt = true)
})
}
}
}
复制代码
必要使用@Observed装饰class Book,Book的属性将被观察。 必要留意的是,@Prop在子组件装饰的状态变量和父组件的数据源是单向同步关系,即ReaderComp中的@Prop book的修改不会同步给父组件Library。而父组件只会在数值有更新的时间(和上一次状态的对比),才会触发UI的重新渲染。
@Observed
class Book {
public id: number;
public title: string;
public pages: number;
public readIt: boolean = false;
constructor(title: string, pages: number) {
this.id = nextId++;
this.title = title;
this.pages = pages;
}
}
复制代码
@Observed装饰的类的实例会被不透明的代理对象包装,此代理可以检测到包装对象内的全部属性更改。如果发生这种情况,此时,代理通知@Prop,@Prop对象值被更新。
@Prop本地初始化不和父组件同步
为了支持@Component装饰的组件复用场景,@Prop支持本地初始化,如许可以让@Prop是否与父组件创建同步关系变得可选。当且仅当@Prop有本地初始化时,从父组件向子组件传递@Prop的数据源才是可选的。
下面的示例中,子组件包罗两个@Prop变量:
@Prop customCounter没有本地初始化,所以必要父组件提供数据源去初始化@Prop,并当父组件的数据源厘革时,@Prop也将被更新;
@Prop customCounter2有本地初始化,在这种情况下,@Prop依旧答应但非强制父组件同步数据源给@Prop。
@Component
struct MyComponent {
@Prop customCounter: number;
@Prop customCounter2: number = 5;
build() {
Column() {
Row() {
Text(`From Main: ${this.customCounter}`).fontColor('#ff6b6565').margin({ left: -110, top: 12 })
}
Row() {
Button('Click to change locally !')
.width(288)
.height(40)
.margin({ left: 30, top: 12 })
.fontColor('#FFFFFF,90%')
.onClick(() => {
this.customCounter2++
})
}
Row() {
Text(`Custom Local: ${this.customCounter2}`).fontColor('#ff6b6565').margin({ left: -110, top: 12 })
}
}
}
}
@Entry
@Component
struct MainProgram {
@State mainCounter: number = 10;
build() {
Column() {
Row() {
Column() {
// customCounter必须从父组件初始化,因为MyComponent的customCounter成员变量缺少本地初始化;此处,customCounter2可以不做初始化。
MyComponent({ customCounter: this.mainCounter })
// customCounter2也可以从父组件初始化,父组件初始化的值会覆盖子组件customCounter2的本地初始化的值
MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })
}
}
Row() {
Column() {
Button('Click to change number')
.width(288)
.height(40)
.margin({ left: 30, top: 12 })
.fontColor('#FFFFFF,90%')
.onClick(() => {
this.mainCounter++
})
}
}
}
}
}
复制代码
@Prop嵌套场景
在嵌套场景下,每一层都要用@Observed装饰,且每一层都要被@Prop吸收,如许才能观察到嵌套场景。
// 以下是嵌套类对象的数据结构。
@Observed
class ClassA {
public title: string;
constructor(title: string) {
this.title = title;
}
}
@Observed
class ClassB {
public name: string;
public a: ClassA;
constructor(name: string, a: ClassA) {
this.name = name;
this.a = a;
}
}
复制代码
以下组件层次布局呈现的是@Prop嵌套场景的数据布局。
@Entry
@Component
struct Parent {
@State votes: ClassB = new ClassB('Hello', new ClassA('world'))
build() {
Column() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
Button('change ClassB name')
.width(312)
.height(40)
.margin(12)
.fontColor('#FFFFFF,90%')
.onClick(() => {
this.votes.name = "aaaaa"
})
Button('change ClassA title')
.width(312)
.height(40)
.margin(12)
.fontColor('#FFFFFF,90%')
.onClick(() => {
this.votes.a.title = "wwwww"
})
Text(this.votes.name)
.fontSize(16)
.margin(12)
.width(312)
.height(40)
.backgroundColor('#ededed')
.borderRadius(20)
.textAlign(TextAlign.Center)
.fontColor('#e6000000')
.onClick(() => {
this.votes.name = 'Bye'
})
Text(this.votes.a.title)
.fontSize(16)
.margin(12)
.width(312)
.height(40)
.backgroundColor('#ededed')
.borderRadius(20)
.textAlign(TextAlign.Center)
.onClick(() => {
this.votes.a.title = "openHarmony"
})
Child1({ vote1: this.votes.a })
}
}
}
}
@Component
struct Child1 {
@Prop vote1: ClassA = new ClassA('');
build() {
Column() {
Text(this.vote1.title)
.fontSize(16)
.margin(12)
.width(312)
.height(40)
.backgroundColor('#ededed')
.borderRadius(20)
.textAlign(TextAlign.Center)
.onClick(() => {
this.vote1.title = 'Bye Bye'
})
}
}
}
复制代码
装饰Map类型变量
分析:
从API version 11开始,@Prop支持Map类型。
在下面的示例中,value类型为Map<number, string>,点击Button改变message的值,视图会随之刷新。
@Component
struct Child {
@Prop value: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])
build() {
Column() {
ForEach(Array.from(this.value.entries()), (item: [number, string]) => {
Text(`${item[0]}`).fontSize(30)
Text(`${item[1]}`).fontSize(30)
Divider()
})
Button('child init map').onClick(() => {
this.value = new Map([[0, "a"], [1, "b"], [3, "c"]])
})
Button('child set new one').onClick(() => {
this.value.set(4, "d")
})
Button('child clear').onClick(() => {
this.value.clear()
})
Button('child replace the first one').onClick(() => {
this.value.set(0, "aa")
})
Button('child delete the first one').onClick(() => {
this.value.delete(0)
})
}
}
}
@Entry
@Component
struct MapSample2 {
@State message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])
build() {
Row() {
Column() {
Child({ value: this.message })
}
.width('100%')
}
.height('100%')
}
}
复制代码
装饰Set类型变量
分析:
从API version 11开始,@Prop支持Set类型。
在下面的示例中,message类型为Set<number>,点击Button改变message的值,视图会随之刷新。
@Component
struct Child {
@Prop message: Set<number> = new Set([0, 1, 2, 3, 4])
build() {
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%')
}
}
@Entry
@Component
struct SetSample11 {
@State message: Set<number> = new Set([0, 1, 2, 3, 4])
build() {
Row() {
Column() {
Child({ message: this.message })
}
.width('100%')
}
.height('100%')
}
}
复制代码
Prop支持团结类型实例
@Prop支持团结类型和undefined和null,在下面的示例中,animal类型为Animals | undefined,点击父组件Zoo中的Button改变animal的属性或者类型,Child中也会对应刷新。
class Animals {
public name: string;
constructor(name: string) {
this.name = name;
}
}
@Component
struct Child {
@Prop animal: Animals | undefined;
build() {
Column() {
Text(`Child's animal is ${this.animal instanceof Animals ? this.animal.name : 'undefined'}`).fontSize(30)
Button('Child change animals into tigers')
.onClick(() => {
// 赋值为Animals的实例
this.animal = new Animals("Tiger")
})
Button('Child change animal to undefined')
.onClick(() => {
// 赋值为undefined
this.animal = undefined
})
}.width('100%')
}
}
@Entry
@Component
struct Zoo {
@State animal: Animals | undefined = new Animals("lion");
build() {
Column() {
Text(`Parents' animals are ${this.animal instanceof Animals ? this.animal.name : 'undefined'}`).fontSize(30)
Child({animal: this.animal})
Button('Parents change animals into dogs')
.onClick(() => {
// 判断animal的类型,做属性的更新
if (this.animal instanceof Animals) {
this.animal.name = "Dog"
} else {
console.info('num is undefined, cannot change property')
}
})
Button('Parents change animal to undefined')
.onClick(() => {
// 赋值为undefined
this.animal = undefined
})
}
}
}
复制代码
常见题目
@Prop装饰状态变量未初始化错误
@Prop必要被初始化,如果没有举行本地初始化的,则必须通过父组件举行初始化。如果举行了本地初始化,那么是可以不通过父组件举行初始化的。
【反例】
@Observed
class Commodity {
public price: number = 0;
constructor(price: number) {
this.price = price;
}
}
@Component
struct PropChild {
@Prop fruit: Commodity; // 未进行本地初始化
build() {
Text(`PropChild fruit ${this.fruit.price}`)
.onClick(() => {
this.fruit.price += 1;
})
}
}
@Entry
@Component
struct Parent {
@State fruit: Commodity[] = [new Commodity(1)];
build() {
Column() {
Text(`Parent fruit ${this.fruit[0].price}`)
.onClick(() => {
this.fruit[0].price += 1;
})
// @Prop本地没有初始化,也没有从父组件初始化
PropChild()
}
}
}
复制代码
【正例】
@Observed
class Commodity {
public price: number = 0;
constructor(price: number) {
this.price = price;
}
}
@Component
struct PropChild1 {
@Prop fruit: Commodity; // 未进行本地初始化
build() {
Text(`PropChild1 fruit ${this.fruit.price}`)
.onClick(() => {
this.fruit.price += 1;
})
}
}
@Component
struct PropChild2 {
@Prop fruit: Commodity = new Commodity(1); // 进行本地初始化
build() {
Text(`PropChild2 fruit ${this.fruit.price}`)
.onClick(() => {
this.fruit.price += 1;
})
}
}
@Entry
@Component
struct Parent {
@State fruit: Commodity[] = [new Commodity(1)];
build() {
Column() {
Text(`Parent fruit ${this.fruit[0].price}`)
.onClick(() => {
this.fruit[0].price += 1;
})
// @PropChild1本地没有初始化,必须从父组件初始化
PropChild1({ fruit: this.fruit[0] })
// @PropChild2本地进行了初始化,可以不从父组件初始化,也可以从父组件初始化
PropChild2()
PropChild2({ fruit: this.fruit[0] })
}
}
}
复制代码
最后
有许多小伙伴不知道学习哪些鸿蒙开发技术?不知道必要重点掌握哪些鸿蒙应用开发知识点?而且学习时频仍踩坑,最终浪费大量时间。所以有一份实用的
鸿蒙(HarmonyOS NEXT)资料
用来跟着学习黑白常有须要的。
这份鸿蒙(HarmonyOS NEXT)资料包罗了鸿蒙开发必掌握的核心知识要点,内容包罗了
(
ArkTS、ArkUI开发组件、Stage模型、多端摆设、分布式应用开发、音频、视频、WebGL、OpenHarmony
多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)
技术知识点。
希望这一份鸿蒙学习资料可以或许给大家带来帮助,有必要的小伙伴自行领取,
限时开源,先到先得~无套路领取!!
获取这份完整版高清学习门路,请点击→
纯血版全套鸿蒙HarmonyOS学习资料
鸿蒙(HarmonyOS NEXT)最新学习门路
HarmonOS基础技能
HarmonOS就业必备技能
HarmonOS多媒体技术
鸿蒙NaPi组件进阶
HarmonOS高级技能
初识HarmonOS内核
实战就业级设备开发
有了门路图,怎么能没有学习资料呢,小编也准备了一份团结鸿蒙官方发布笔记整理收纳的一套系统性的
鸿蒙(OpenHarmony )学习手册(共计1236页)
与
鸿蒙(OpenHarmony )开发入门教学视频
,内容包罗:
ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习门路,请点击→
纯血版全套鸿蒙HarmonyOS学习资料
《鸿蒙 (OpenHarmony)开发入门教学视频》
《鸿蒙生态应用开发V2.0白皮书》
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发情况搭建
《鸿蒙开发基础》
ArkTS语言
安装DevEco Studio
运用你的第一个ArkTS应用
ArkUI声明式UI开发
.……
《鸿蒙开发进阶》
Stage模型入门
网络管理
数据管理
电话服务
分布式应用开发
通知与窗口管理
多媒体技术
安全技能
任务管理
WebGL
国际化开发
应用测试
DFX面向未来计划
鸿蒙系统移植和裁剪定制
……
《鸿蒙进阶实战》
ArkTS实践
UIAbility应用
网络案例
……
获取以上完整鸿蒙HarmonyOS学习资料,请点击→
纯血版全套鸿蒙HarmonyOS学习资料
总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑衅,也是一个机会。只有积极应对厘革,不断学习和提升本身,他们才能在这个厘革的期间中立于不败之地。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4