前言
在列表組件使用的時候恬汁,如List伶椿、Grid、WaterFlow等氓侧,循環(huán)渲染時都會使用到ForEach或者LazyForEach脊另,當(dāng)然了,也有單獨(dú)使用的場景约巷,如下偎痛,一個很簡單的列表組件使用,這種使用方式独郎,在官方的很多案例中也多次出現(xiàn)踩麦,相信在實際的開發(fā)中多多少少也會存在。
List({ space: 20, initialIndex: 0 }) {
ForEach(["條目1", "條目2", "條目3", "條目4", "條目5", "條目6"], (item: string) => {
ListItem() {
Text(item)
.width('100%')
.height(50)
.fontSize(16)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Orange)
}
}, (item: string) => item)
}.padding({ left: 20, right: 20 })
以上的代碼氓癌,看上去也沒啥問題谓谦,UI也能正常的展示出來,如下圖:
仿佛這一切都是正確的顽铸,但是茁计,以上的代碼會存在一定的問題,那就是渲染非預(yù)期谓松, 我們繼續(xù)驗證問題所在星压,增加一個按鈕,用來添加數(shù)據(jù)鬼譬,當(dāng)然了這里需要把數(shù)據(jù)源提取至成員變量娜膘,并用@State裝飾器進(jìn)行修飾:
@State list: string[] = ["條目1", "條目2", "條目3", "條目4", "條目5", "條目6"]
build() {
Column() {
Button("追加數(shù)據(jù)").onClick(() => {
this.list.push("條目七")
this.list.push("條目八")
})
List({ space: 20, initialIndex: 0 }) {
ForEach(this.list, (item: string) => {
ListItem() {
Text(item)
.width('100%')
.height(50)
.fontSize(16)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Orange)
}
}, (item: string) => item)
}.padding({ left: 20, right: 20 })
.margin({ top: 20 })
}
}
當(dāng)我們點擊追加數(shù)據(jù)按鈕時,正常的情況會是优质,數(shù)組中增加數(shù)據(jù)竣贪,驅(qū)動UI更新军洼,List組件應(yīng)該會增加【條目七,條目八】兩條數(shù)據(jù)演怎。
確實匕争,點擊后,UI發(fā)生了變化爷耀,列表中增加了兩條數(shù)據(jù):
有問題嗎甘桑? 說了一大堆,程序這不執(zhí)行挺正常的歹叮,哎跑杭,稍安勿躁,我們再次點擊一下咆耿,正常的程序德谅,會再次增加兩條數(shù)據(jù),對吧萨螺?
但是窄做,問題來了,沒有增加P加亍=摺!惹盼,點擊一百次也沒有增加庸汗。
難道是重復(fù)的數(shù)據(jù)不能重復(fù)添加?這就很扯了吧手报,列表中不能出現(xiàn)重復(fù)的數(shù)據(jù)蚯舱,這在任何一個系統(tǒng)中都是聞所未聞的奇觀。
顯然這些問題都不是掩蛤,問題的原因就在于枉昏,循環(huán)的第三個參數(shù):keyGenerator。
本文的主要內(nèi)容如下:
1揍鸟、了解循環(huán) ForEach/LazyForEach三個參數(shù)
2兄裂、了解 鍵值生成規(guī)則
3、禁止渲染非預(yù)期情況
4阳藻、正確使用鍵值
5晰奖、使用相關(guān)總結(jié)
一、了解循環(huán)ForEach/LazyForEach三個參數(shù)
(arr: Array<any>, itemGenerator: (item: any, index: number) => void, keyGenerator?: (item: any, index: number) => string): ForEachAttribute;
第一個參數(shù)arr是數(shù)據(jù)源腥泥,用來渲染UI的數(shù)據(jù)匾南,非常重要,渲染多少數(shù)據(jù)蛔外,動態(tài)增加數(shù)據(jù)蛆楞,都是和它有著直接的關(guān)系溯乒,可以是任何類型的數(shù)組源,比如對象豹爹,字符串裆悄,數(shù)值,都可以臂聋。
第二個參數(shù)itemGenerator灯帮,是組件生成函數(shù),目的為數(shù)組中的每個元素創(chuàng)建對應(yīng)的組件逻住,它是和第一個數(shù)據(jù)源是一一對應(yīng)的。
第三個參數(shù)keyGenerator迎献,是鍵值生成函數(shù)瞎访,為數(shù)據(jù)源arr的每個數(shù)組項生成唯一且持久的鍵值,其返回值吁恍,可以自己定義扒秸,如果自己定義,一定要是唯一的冀瓦,如果不定義伴奥,會是默認(rèn)的:(item: T, index: number) => { return index + '__' + JSON.stringify(item); },默認(rèn)的也能滿足大部分的需求翼闽,所以拾徙,在實際的開發(fā)中,如果你很難決定唯一感局,那么直接用默認(rèn)的就行尼啡。在前言中的問題,就是因為鍵值不唯一造成的询微。
二崖瞭、了解鍵值生成規(guī)則
通過了解循環(huán)的三個參數(shù),我們已經(jīng)知道了撑毛,系統(tǒng)會為我們提供設(shè)置鍵值的函數(shù)參數(shù)书聚,可以使用自定義的,當(dāng)然也可以使用默認(rèn)的鍵值生成規(guī)則藻雌,也就是item: Object, index: number) => { return index + '__' + JSON.stringify(item); }雌续。
在實際的渲染過程中,每個數(shù)組元素生成一個唯一且持久的鍵值蹦疑,用來標(biāo)記相對應(yīng)的組件西雀,當(dāng)鍵值有變化時,ArkUI框架會認(rèn)為歉摧,當(dāng)前數(shù)組元素替換或修改艇肴,會根據(jù)新的鍵值重新創(chuàng)建一個新的組件腔呜。
鍵值的生成規(guī)則,直接會影響著數(shù)據(jù)渲染的UI再悼,因為第二個參數(shù)itemGenerator函數(shù)會根據(jù)鍵值生成規(guī)則為數(shù)據(jù)源的每個數(shù)組項創(chuàng)建組件核畴。
在前言的Demo中,可以發(fā)現(xiàn)冲九,每個組件的鍵值為當(dāng)前的數(shù)據(jù)源谤草,當(dāng)不同數(shù)組項按照鍵值生成規(guī)則生成的鍵值相同時,框架認(rèn)為是未定義的莺奸,此時不再創(chuàng)建新的組件丑孩, 也就是點擊不會再次創(chuàng)建組件的原因。
當(dāng)然了灭贷,還有一種情況温学,那就是,在已有的數(shù)據(jù)上進(jìn)行修改甚疟,比如有三條數(shù)據(jù)仗岖,把第三條數(shù)據(jù)修改為新的數(shù)據(jù)源,這種情況览妖,前兩個數(shù)據(jù)轧拄,F(xiàn)orEach會復(fù)用進(jìn)行渲染,第三個則會為該數(shù)組項創(chuàng)建了一個新的組件讽膏。
三檩电、禁止渲染非預(yù)期情況
什么叫渲染非預(yù)期?前言中的Demo就是一個典型的案例府树,存在相同鍵值是嗜,因此不會創(chuàng)建新組件,在實際的開發(fā)中挺尾,使用ForEach時應(yīng)盡量避免最終鍵值生成規(guī)則中包含index鹅搪,或者使用不唯一的規(guī)則作為鍵值。
四遭铺、正確使用鍵值
首先丽柿,必須滿足鍵值的唯一性,這一點毋庸置疑魂挂,必須要設(shè)置正確甫题,如果使用的是對象,強(qiáng)烈建議涂召,使用對象中的唯一值坠非,比如id作為鍵值。
如果是使用基本類型的數(shù)據(jù)作為鍵值果正,一定要確保數(shù)組中的元素是沒有重復(fù)的炎码,否則就會出現(xiàn)前言Demo中的問題盟迟,另外,在使用基本類型鍵值潦闲,F(xiàn)orEach在改變數(shù)據(jù)源后會重新創(chuàng)建組件攒菠,這會帶來一定的性能損耗問題。
根據(jù)官方的解讀歉闰,在使用ForEach的時候辖众,盡量不要與LazyForEach混合使用,這是官方所不推薦的和敬,切記凹炸!
五、使用相關(guān)總結(jié)
為了使得數(shù)據(jù)渲染正確昼弟,請一定要確保第三個參數(shù)鍵值的唯一性还惠,另外除非必要,不推薦將第三個參數(shù)KeyGenerator函數(shù)處于缺省狀態(tài)私杜,以及在鍵值生成規(guī)則中包含數(shù)據(jù)項索引index。