課程地址: 黑馬程序員HarmonyOS4+NEXT星河版入門到企業級實戰教程,一套精通鴻蒙應用開發
(本篇筆記對應課程第 17 節)
P17《16.Ark-狀態管理@Prop @Link @Provide @Consume》
將上一節寫出的代碼進行功能模塊封裝:1、任務進度卡片為一個組件;2、新增任務按鈕與任務列表展示為一個組件
1、任務進度卡片抽取為一個組件
2、使用這個組件并傳遞參數,此時 struct PropPage為父組件, TaskStatistics 為子組件。傳遞參數這里報錯了:因為父組件和子組件里同時都用 @State 裝飾器聲明了同樣的變量。
將子組件中聲明變量前的 @State 去掉,發現不報錯了:
此時發現父組件中數據變化了,但子組件并不知道這些數據變化了:
如需要父組件與子組件中的數據進行聯動,就需要使用 @Prop 或者 @Link 裝飾器。兩者之間的區別是:@Prop 是數據的單向同步,即只會在父組件中修改數據影響子組件,反之不會;實現原理是將數據copy了一份傳遞給了子組件;而 @Link是數據的雙向同步,父組件修改數據會影響子組件,子組件中修改數據也會影響父組件,實現原理是將數據的引用傳遞給了子組件,父組件與子組件修改的是同一份數據。
在子組件中的變量前加上@Prop 裝飾,此時發現提示:**@Prop 修飾的變量不能再初始化。因為@Prop是期望父組件傳遞過來數據,所以不需要再自己初始化了。**刪去初始化賦值。
此時發現任務管理子組件中的數據可以更新了。這樣就實現了父組件到子組件的單向數據聯動。
將新增任務與任務列表部分抽取為子組件:將其需要的state數據、操作數據的方法以及自定義的構建函數 deleteBtn 全部放到這個子組件中去,因為它使用到了這些功能:
使用這個子組件并且父組件中向其傳遞參數:
子組件中相應的數據改為 @Prop 聲明:
由于需要在子組件中也改變 總任務數量與已完成任務數量的值,所以需要用 @Link 傳遞這兩個數據:
此時發現父組件中傳遞數據報錯了:
因為 @Link 需要傳遞一個引用給子組件,用變量前面加上 $ 的方式代表傳遞這個變量的引用,這樣子組件對數據的修改父組件也可以感知到了:
如果子組件不需要對數據進行修改,只用數據做渲染展示,那么使用 @Prop 傳遞;如果子組件需要對數據進行修改,那么使用 @Link 傳遞。
官方文檔中寫 @Prop可以初始化,還寫 @Prop 允許父組件是 數組,兒子是元素,但實際上是不行的!
驗證一下對象類型:
用 @Prop 裝飾一個對象類型數據,會發現有提示:@Prop 裝飾的數據必須是 string、number、boolean類型。
@Link 是允許傳遞對象的:
@Link 支持傳遞對象類型,而@Prop不支持傳遞對象類型,但可以傳遞對象的屬性。
@Provide 與 @Consume :
父組件中改為 @Provide,子組件中改為 @Consume,此時發現報錯了:
因為@Provide不需要顯示傳遞參數,將傳參去掉:
同時子組件中使用 @Consume 來接收:
@Prop 與 @Link 的選擇:如果子組件不需要修改數據,只用數據來渲染展示,則使用 @Prop ;如果子組件需要修改數據,則使用 @Link。
@Provide @Consume 與 @Prop @Link 的選擇:如果是父子組件傳遞,沒必要使用@Provide @Consume ,因為它不需要顯示傳遞參數,內部做了維護,肯定會造成一些性能的損耗;所以父子組件間傳遞盡量使用 @Prop @Link ;跨級組件傳遞可以使用 @Provide @Consume 。
注意:@Provide @Consume 祖組件與后代組件中傳遞的數據是雙向同步的。
實踐:
1、拆分子組件并使用 @Prop @Link 傳遞數據:
class Task {static id:number = 1// 任務名稱name:string = `任務${Task.id++}`// 任務狀態:是否完成finished:boolean = false
}@Styles function cardStyle(){.width('100%').height(120).padding(10).backgroundColor('#fff').borderRadius(8)
}@Entry
@Component
struct Index {// 總任務數量@State totalTask:number = 0// 已完成任務數量@State finishTask:number = 0// 任務列表@State tasks:Task[] = []build() {Row() {Column() {// 1、任務進度卡片TaskStatistics({ totalTask:this.totalTask, finishTask:this.finishTask })// 2、使用任務列表子組件TaskList({ totalTask:$totalTask, finishTask:$finishTask })}.width('100%').height('100%').justifyContent(FlexAlign.Start)}.height('100%').width('100%').padding({top:20,bottom :20, left:10,right:10}).backgroundColor('#efefef')}}// 任務進度卡片子組件
@Component
struct TaskStatistics{@Prop finishTask:number@Prop totalTask:numberbuild(){Row(){Text('任務進度:').fontSize(22).fontWeight(FontWeight.Bold)Stack(){Progress({value : this.finishTask,total : this.totalTask,type : ProgressType.Ring})Row(){Text(this.finishTask.toString())Text(`/${this.totalTask.toString()}`)}}}.cardStyle().justifyContent(FlexAlign.SpaceEvenly)}
}// 新增任務與任務列表子組件
@Component
struct TaskList{// 總任務數量@Link totalTask:number// 已完成任務數量@Link finishTask:number// 任務列表@State tasks:Task[] = []handleTaskChange(){// 更新任務總數量this.totalTask = this.tasks.length// 更新已完成任務數量this.finishTask = this.tasks.filter(item => item.finished).length}build() {Column(){// 2、新增任務按鈕Button('新增任務').width(200).margin({top:20, bottom:20}).onClick(()=>{// 新增任務this.tasks.push(new Task())// 更新任務總數量// this.totalTask = this.tasks.lengththis.handleTaskChange()})// 3、任務列表展示List(){ForEach(this.tasks,(item:Task,index)=>{ListItem(){Row(){Text(item.name)Checkbox().select(item.finished).onChange(val => {// 更新任務狀態item.finished = val// 更新已完成任務數量// this.finishTask = this.tasks.filter(item => item.finished).lengththis.handleTaskChange()})}.cardStyle().height(60).margin({bottom:10}).justifyContent(FlexAlign.SpaceBetween)}.swipeAction({ end: this.deleteBtn(index)})})}.layoutWeight(1)}}@Builder deleteBtn(index){Button(){Image($r('app.media.icon_delete')).width(30).fillColor(Color.Red)}.width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(6).onClick(() => {this.tasks.splice(index,1)this.handleTaskChange()})}
}
2、驗證 @Prop 不支持對象類型,僅支持對象屬性的傳遞;而 @Link 支持傳遞對象類型:
class Task {static id:number = 1// 任務名稱name:string = `任務${Task.id++}`// 任務狀態:是否完成finished:boolean = false
}class Stat {// 總任務數量totalTask:number = 0// 已完成任務數量finishTask:number = 0
}@Styles function cardStyle(){.width('100%').height(120).padding(10).backgroundColor('#fff').borderRadius(8)
}@Entry
@Component
struct Index {// 總任務數量// @State totalTask:number = 0// 已完成任務數量// @State finishTask:number = 0@State stat:Stat = new Stat()// 任務列表@State tasks:Task[] = []build() {Row() {Column() {// 1、任務進度卡片TaskStatistics({ totalTask:this.stat.totalTask, finishTask:this.stat.finishTask })// 2、使用任務列表子組件// TaskList({ totalTask:$totalTask, finishTask:$finishTask })TaskList({ stat:$stat })}.width('100%').height('100%').justifyContent(FlexAlign.Start)}.height('100%').width('100%').padding({top:20,bottom :20, left:10,right:10}).backgroundColor('#efefef')}}// 任務進度卡片子組件
@Component
struct TaskStatistics{@Prop finishTask:number@Prop totalTask:numberbuild(){Row(){Text('任務進度:').fontSize(22).fontWeight(FontWeight.Bold)Stack(){Progress({value : this.finishTask,total : this.totalTask,type : ProgressType.Ring})Row(){Text(this.finishTask.toString())Text(`/${this.totalTask.toString()}`)}}}.cardStyle().justifyContent(FlexAlign.SpaceEvenly)}
}// 新增任務與任務列表子組件
@Component
struct TaskList{// 總任務數量// @Link totalTask:number// 已完成任務數量// @Link finishTask:number@Link stat:Stat// 任務列表@State tasks:Task[] = []handleTaskChange(){// 更新任務總數量// this.totalTask = this.tasks.lengththis.stat.totalTask = this.tasks.length// 更新已完成任務數量// this.finishTask = this.tasks.filter(item => item.finished).lengththis.stat.finishTask = this.tasks.filter(item => item.finished).length}build() {Column(){// 2、新增任務按鈕Button('新增任務').width(200).margin({top:20, bottom:20}).onClick(()=>{// 新增任務this.tasks.push(new Task())// 更新任務總數量// this.totalTask = this.tasks.lengththis.handleTaskChange()})// 3、任務列表展示List(){ForEach(this.tasks,(item:Task,index)=>{ListItem(){Row(){Text(item.name)Checkbox().select(item.finished).onChange(val => {// 更新任務狀態item.finished = val// 更新已完成任務數量// this.finishTask = this.tasks.filter(item => item.finished).lengththis.handleTaskChange()})}.cardStyle().height(60).margin({bottom:10}).justifyContent(FlexAlign.SpaceBetween)}.swipeAction({ end: this.deleteBtn(index)})})}.layoutWeight(1)}}@Builder deleteBtn(index){Button(){Image($r('app.media.icon_delete')).width(30).fillColor(Color.Red)}.width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(6).onClick(() => {this.tasks.splice(index,1)this.handleTaskChange()})}
}
3、使用 @Provide 和 @Consume:
class Task {static id:number = 1// 任務名稱name:string = `任務${Task.id++}`// 任務狀態:是否完成finished:boolean = false
}class Stat {// 總任務數量totalTask:number = 0// 已完成任務數量finishTask:number = 0
}@Styles function cardStyle(){.width('100%').height(120).padding(10).backgroundColor('#fff').borderRadius(8)
}@Entry
@Component
struct Index {// 總任務數量// @State totalTask:number = 0// 已完成任務數量// @State finishTask:number = 0// @State stat:Stat = new Stat()@Provide stat:Stat = new Stat()// 任務列表@State tasks:Task[] = []build() {Row() {Column() {// 1、任務進度卡片TaskStatistics()// 2、使用任務列表子組件// TaskList({ totalTask:$totalTask, finishTask:$finishTask })TaskList()}.width('100%').height('100%').justifyContent(FlexAlign.Start)}.height('100%').width('100%').padding({top:20,bottom :20, left:10,right:10}).backgroundColor('#efefef')}}// 任務進度卡片子組件
@Component
struct TaskStatistics{/*@Prop finishTask:number@Prop totalTask:number*/@Consume stat:Statbuild(){Row(){Text('任務進度:').fontSize(22).fontWeight(FontWeight.Bold)Stack(){Progress({/*value : this.finishTask,total : this.totalTask,*/value : this.stat.finishTask,total : this.stat.totalTask,type : ProgressType.Ring})Row(){/*Text(this.finishTask.toString())Text(`/${this.totalTask.toString()}`)*/Text(this.stat.finishTask.toString())Text(`/${this.stat.totalTask.toString()}`)}}}.cardStyle().justifyContent(FlexAlign.SpaceEvenly)}
}// 新增任務與任務列表子組件
@Component
struct TaskList{// 總任務數量// @Link totalTask:number// 已完成任務數量// @Link finishTask:number// @Link stat:Stat@Consume stat:Stat// 任務列表@State tasks:Task[] = []handleTaskChange(){// 更新任務總數量// this.totalTask = this.tasks.lengththis.stat.totalTask = this.tasks.length// 更新已完成任務數量// this.finishTask = this.tasks.filter(item => item.finished).lengththis.stat.finishTask = this.tasks.filter(item => item.finished).length}build() {Column(){// 2、新增任務按鈕Button('新增任務').width(200).margin({top:20, bottom:20}).onClick(()=>{// 新增任務this.tasks.push(new Task())// 更新任務總數量// this.totalTask = this.tasks.lengththis.handleTaskChange()})// 3、任務列表展示List(){ForEach(this.tasks,(item:Task,index)=>{ListItem(){Row(){Text(item.name)Checkbox().select(item.finished).onChange(val => {// 更新任務狀態item.finished = val// 更新已完成任務數量// this.finishTask = this.tasks.filter(item => item.finished).lengththis.handleTaskChange()})}.cardStyle().height(60).margin({bottom:10}).justifyContent(FlexAlign.SpaceBetween)}.swipeAction({ end: this.deleteBtn(index)})})}.layoutWeight(1)}}@Builder deleteBtn(index){Button(){Image($r('app.media.icon_delete')).width(30).fillColor(Color.Red)}.width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(6).onClick(() => {this.tasks.splice(index,1)this.handleTaskChange()})}
}