鸿蒙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 初始化规则图示  

zh-cn_image_0000001552972029

观察变化和行为表现

观察变化

@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的接口setFullYearsetMonthsetDatesetHourssetMinutessetSecondssetMillisecondssetTimesetUTCFullYearsetUTCMonthsetUTCDatesetUTCHourssetUTCMinutessetUTCSecondssetUTCMilliseconds 更新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的接口setcleardelete 更新Map的值。详见装饰Map类型变量。

  • 当装饰的变量是Set时,可以观察到Set整体的赋值,同时可通过调用Set的接口addcleardelete 更新Set的值。详见装饰Set类型变量。

框架行为

要理解@Prop变量值初始化和更新机制,有必要了解父组件和拥有@Prop变量的子组件初始渲染和更新流程。

  1. 初始渲染:

    1. 执行父组件的build()函数将创建子组件的新实例,将数据源传递给子组件;
    2. 初始化子组件@Prop装饰的变量。
  2. 更新:

    1. 子组件@Prop更新时,更新仅停留在当前子组件,不会同步回父组件;
    2. 当父组件的数据源更新时,子组件的@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 })}}
}

在上面的示例中:

  1. CountDownComponent子组件首次创建时其@Prop装饰的count变量将从父组件@State装饰的countDownStartValue变量初始化;

  2. 按“+1”或“-1”按钮时,父组件的@State装饰的countDownStartValue值会变化,这将触发父组件重新渲染,在父组件重新渲染过程中会刷新使用countDownStartValue状态变量的UI组件并单向同步更新CountDownComponent子组件中的count值;

  3. 更新count状态变量值也会触发CountDownComponent的重新渲染,在重新渲染过程中,评估使用count状态变量的if语句条件(this.count > 0),并执行true分支中的使用count状态变量的UI组件相关描述来更新Text组件的UI显示;

  4. 当按下子组件CountDownComponent的“Try again”按钮时,其@Prop变量count将被更改,但是count值的更改不会影响父组件的countDownStartValue值;

  5. 父组件的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对象值被更新。

Video-prop-UsageScenario-one

@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++})}}}}
}

Video-prop-UsageScenario-two

@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'})}}
}

Video-prop-UsageScenario-three

装饰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(() => {// 赋值为undefinedthis.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(() => {// 赋值为undefinedthis.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学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/283523.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JSONP 实现跨域请求案例

后端使用 express 搭建&#xff0c;案例代码如下&#xff1a; const express require(express)const app express() const PORT 3000app.get(/data, (req, res) > {const jsonData {name: Alan,age: 666,city: GD}const callback req.query.callback // 获取前端中的回…

MNN 执行推理(九)

系列文章目录 MNN createFromBuffer&#xff08;一&#xff09; MNN createRuntime&#xff08;二&#xff09; MNN createSession 之 Schedule&#xff08;三&#xff09; MNN createSession 之创建流水线后端&#xff08;四&#xff09; MNN Session 之维度计算&#xff08;五…

ROS2从入门到精通0-3:VSCode 搭建 ROS2 工程环境

目录 0 专栏介绍1 Ubuntu下安装VSCode1.1 基本安装1.2 将VSCode添加到侧边栏 2 VSCode集成相关插件3 VSCode运行ROS2环境步骤3.1 安装编译依赖项3.2 创建工作空间和源码空间3.3 启动VSCode与配置 4 测试工程环境4.1 C版本4.2 Python版本 0 专栏介绍 本专栏旨在通过对ROS2的系统…

【每日一问】IOS手机上Charles证书过期怎么办?

1、如何查看证书是否过期? 设置>通用>VPN与设备管理 2、在Charles中重置证书 步骤1&#xff1a;重置证书 Help>SSL Proxying>Reset Charles Root Certificate… 步骤2&#xff1a;在浏览器中&#xff0c;下载证书 首先&#xff0c;手机连上代理&#xff0c;然…

JavaScript 权威指南第七版(GPT 重译)(二)

第四章&#xff1a;表达式和运算符 本章记录了 JavaScript 表达式以及构建许多这些表达式的运算符。表达式 是 JavaScript 的短语&#xff0c;可以 评估 以产生一个值。在程序中直接嵌入的常量是一种非常简单的表达式。变量名也是一个简单表达式&#xff0c;它评估为分配给该变…

阿里云2核4G云服务器ECS和轻量应用服务器价格表

阿里云2核4G服务器租用优惠价格&#xff0c;轻量2核4G服务器165元一年、u1服务器2核4G5M带宽199元一年、云服务器e实例30元3个月&#xff0c;活动链接 aliyunfuwuqi.com/go/aliyun 活动链接如下图&#xff1a; 阿里云2核4G服务器优惠价格 轻量应用服务器2核2G4M带宽、60GB高效…

面试算法-82-不同路径

题目 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&#xff1f; …

律师如何看待项目管理中的技术风险

大家好&#xff0c;我是不会魔法的兔子&#xff0c;是一枚北京的执业律师&#xff0c;创建[项目管理者的法小院儿]&#xff0c;持续从法律的角度分享项目管理中的风险问题及预防&#xff0c;让项目管理者能够提早发现与解决项目执行过程中的风险&#xff0c;同时欢迎大家一起交…

【C语言】数据在内存中的存储(包含大小端字节序问题)~

一、前言 我们在刚开始学习C语言的时候&#xff0c;就接触到了很多数据的不同类型。我们也知道&#xff0c;数据是存储在一块内存空间的&#xff0c;且我们只知道数据的类型决定着&#xff0c;该数据在内存中所占内存空间的大小&#xff0c;且超过一个字节的数据在内存中存储的…

手撕算法-接雨水

描述 分析 i位置能积累的雨水量&#xff0c;等于其左右两边最大高度的最小值。为了能获取i位置左右两边的最大高度。使用动态规划。两个dp数组&#xff1a; leftMaxrightMax 其中 leftMax[i] 代表i位置左边的最大高度rightMax[i] 代表i位置右边的最大高度 初始状态&#x…

Nacos源码流程图

1.Nacos1.x版本服务注册与发现源码 流程图地址&#xff1a;https://www.processon.com/view/link/634695eb260d7157a7bc6adb 2.Nacos2.x版本服务注册与发现源码 流程图地址&#xff1a;https://www.processon.com/view/link/634695fb260d7157a7bc6ae0 3.Nacos2.x版本GRPC…

Visual Studio 2013 - 高亮设置括号匹配 (方括号)

Visual Studio 2013 - 高亮设置括号匹配 [方括号] 1. 高亮设置 括号匹配 (方括号)References 1. 高亮设置 括号匹配 (方括号) 工具 -> 选项… -> 环境 -> 字体和颜色 References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

React - 实现菜单栏滚动

简介 本文将会基于react实现滚动菜单栏功能。 技术实现 实现效果 点击菜单&#xff0c;内容区域会自动滚动到对应卡片。内容区域滑动&#xff0c;指定菜单栏会被选中。 ScrollMenu.js import {useRef, useState} from "react"; import ./ScrollMenu.css;export co…

网络原理(5)——IP协议(网络层)

目录 一、IP协议报头介绍 1、4位版本 2、4位首部长度 3、8位服务器类型 4、16位总长度 5、16位标识位 6、3位标志位 7、13位偏移量 8、8位生存空间 9、8位协议 10、16位首部检验和 11、32位源IP地址 12、32位目的IP地址 二、IP协议如何管理地址&#xff1f; 1、动…

python 空间距离计算

目录 python 空间距离计算 已知两点&#xff0c;画三角形 批量矩阵计算 python 空间距离计算 要在空间中找到一个点&#xff0c;使其位于点 b 和 c 之间的连线上&#xff0c;并且与点 b 的距离等于点 a 到点 b 的距离的2倍。 import numpy as npif __name__ __main__:a …

链路聚合的应用场景

STP&#xff1a;聚合后计算 物理端口的速率叠加 链路聚合、端口聚合&#xff0c;链路捆绑 把多根链路逻辑捆绑在一起&#xff0c;当STP进行拓扑计算时此时会按照聚合后链路计算链路聚合物理链路速率的叠加最多可以用8个链路同时捆绑 1.提升带宽 2.增加网络的高可用性&#…

【计算机网络】基本概念

基本概念 IP 地址端口号协议协议分层封装分用客户端服务器请求和响应两台主机之间的网络通信流程 IP 地址 概念&#xff1a;IP 地址主要是用于唯一标识网络主机、其他网络设备&#xff08;如路由器&#xff09;的网络地址。简单来说&#xff0c;IP地址用来唯一定位主机。格式&…

ky10.aarch64安装Jenkins

参考地址&#xff1a;《安装部署 Jenkins》 前言 有war包和rpm两种安装方式&#xff0c;如果是长期使用更加推荐rpm的安装方式&#xff0c;可以更好的管理Jenkins&#xff1b; 我此次安装jenkins主要用于测试和简单的个人使用&#xff0c;所以选择更轻便的war安装。 1 下载J…

校招应聘流程讲解

在整个应聘流程中&#xff0c;记得保持积极的态度、认真准备面试&#xff0c;同时也要对自己的能力和经验有清晰的认识&#xff0c;这样才能在竞争激烈的校园招聘中脱颖而出&#xff0c;成功获得心仪的工作机会. 1. 校招资源获取 想要参加校招&#xff0c;首先需要获取校招资…

Mac上玩《赛博朋克2077》mac电脑怎么玩这个游戏

X用户crushovitz_b最近发现&#xff0c;在《赛博朋克2077》游戏主菜单页面&#xff0c;将鼠标停在版本号选项卡上面足够长时间&#xff0c;就会发现游戏当前的版本号由2.12变为了2.0.77&#xff0c;这是对游戏标题2077的致敬彩蛋。 《赛博朋克2077》的叙事总监兼续集副总监Pawe…