JS UI框架下,List組件運(yùn)行時(shí)的內(nèi)存優(yōu)化
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
每種編程語言都有它的內(nèi)存管理機(jī)制,不同設(shè)備上可用內(nèi)存不同,分配給JS引擎可用的內(nèi)存范圍也不同。例如運(yùn)行內(nèi)存在128MB以下的輕量設(shè)備,對應(yīng)JS引擎的可用內(nèi)存范圍為48 64KB。本文也將以此類設(shè)備為例進(jìn)行分析。
當(dāng)整個(gè)頁面渲染比較復(fù)雜時(shí),JS運(yùn)行內(nèi)存峰值就可能會(huì)超過JS引擎分配到的最大可用內(nèi)存,導(dǎo)致頁面無法渲染。
List組件是JS UI框架下最基本的容器組件之一,提供了一系列相同寬度的列表項(xiàng)。在應(yīng)用開發(fā)過程中,經(jīng)常會(huì)使用List容器組件來呈現(xiàn)大量的數(shù)據(jù)。所以,在List組件應(yīng)用的開發(fā)過程中,開發(fā)者應(yīng)充分考慮內(nèi)存優(yōu)化問題。
本期,我們將通過List組件開發(fā)一個(gè)通訊錄頁面,并采用list+for的方案對整個(gè)頁面進(jìn)行優(yōu)化,達(dá)到減小JS運(yùn)行內(nèi)存的目的。
一、代碼實(shí)現(xiàn)
如下所示,是一張簡單的通訊錄頁面,包含了姓名、電話及對應(yīng)圖片。下面將通過兩種實(shí)現(xiàn)方式來對比代碼性能。

圖1 簡單的通訊錄頁面
方法一:直接書寫對應(yīng)的組件頁面
使用HML直接撰寫整個(gè)組件頁面的內(nèi)容,代碼如下:
- <div class="container">
- <list class="list">
- <list-item class="list-item">
- <image class="image" src="/common/1.png"></image>
- <div class="info">
- <text class="text">張三</text>
- <marquee class="detail">電話:+86 130XXXXXXXX</marquee>
- </div>
- </list-item>
- <list-item class="list-item">
- <image class="image" src="/common/1.png"></image>
- <div class="info">
- <text class="text">李四</text>
- <marquee class="detail">電話:027-6128XXXX</marquee>
- </div>
- </list-item>
- <list-item class="list-item">
- <image class="image" src="/common/1.png"></image>
- <div class="info">
- <text class="text">王五</text>
- <marquee class="detail">電話:+86 150xxxxxx</marquee>
- </div>
- </list-item>
- <list-item class="list-item">
- <image class="image" src="/common/1.png"></image>
- <div class="info">
- <text class="text">小明</text>
- <marquee class="detail">電話:+86 130XXXXXXXX</marquee>
- </div>
- </list-item>
- <list-item class="list-item">
- <image class="image" src="/common/2.png"></image>
- <div class="info">
- <text class="text">小紅</text>
- <marquee class="detail">電話:+86 180XXXX </marquee>
- </div>
- </list-item>
- ...
- </list>
- <input value="非for" on:click="changeNextPage" class="button"></input>
- </div>
方法二:通過for指令來書寫對應(yīng)的組件頁面
針對方法一中的實(shí)現(xiàn),采用for指令來改進(jìn),使對應(yīng)頁面更簡潔,對應(yīng)修改后代碼如下:
- <div class="container">
- <list class="list" on:scrollend="changeNextPage">
- <list-item class="list-item" for = "{{listData}}">
- <image class = "image" src = "/common/{{$item.src}}"></image>
- <div class = "info">
- <text class="text">{{$item.name}}</text>
- <marquee class = "detail">電話: {{$item.phone}}</marquee>
- </div>
- </list-item>
- </list>
- </div>
對應(yīng)的for指令的渲染數(shù)組代碼如下:
- export default {
- data: {
- listData:[]
- },
- onInit() {
- for (var i = 0; i < 10; i++) {
- this.listData.push({'name':'張三', src :'1.png', phone:"+86 130XXXXXX"});
- this.listData.push({'name':'李四', src :'2.png', phone:"027-6128XXXX"});
- this.listData.push({'name':'王五', src :'1.png', phone:"+ 86 150XXXXXX"});
- this.listData.push({'name':'小明', src :'1.png', phone:'+86 130XXXXXX'});
- this.listData.push({'name':'小紅', src :'2.png', phone:'+86 180XXXX'});
- }
- }
- }
二、性能測試
這里,我們針對不同的item數(shù)量,分別測試了以上兩種實(shí)現(xiàn)方式的JS運(yùn)行性能,JS運(yùn)行內(nèi)存與JS運(yùn)行內(nèi)存峰值如下圖所示:

圖2 兩種方法的內(nèi)存占用
由上表測試數(shù)據(jù)可以看出,采用方法二進(jìn)行渲染,JS運(yùn)行內(nèi)存會(huì)出現(xiàn)比較大的浮動(dòng)。但是使用方法一,對應(yīng)的JS運(yùn)行內(nèi)存基本保持不變,這種差異是由兩種不同的頁面加載渲染機(jī)制造成的。
方法一的加載機(jī)制: 對整個(gè)頁面一次性全部進(jìn)行加載,在加載完成后,會(huì)對List組件頁面占用的JS運(yùn)行內(nèi)存進(jìn)行釋放。頁面后續(xù)滑動(dòng),并不會(huì)觸發(fā)組件的解析,從而不會(huì)影響JS運(yùn)行時(shí)內(nèi)存數(shù)據(jù)。
方法二的加載機(jī)制: 每次滑動(dòng)屏幕會(huì)加載當(dāng)前顯示頁面以及緩存部分的item,超出屏幕之外的item會(huì)對其占用的JS內(nèi)存資源進(jìn)行回收。當(dāng)List組件頁面下滑到新的item時(shí)會(huì)重新創(chuàng)建請求,這種情況下會(huì)降低一部分的滑動(dòng)性能,但是可以實(shí)現(xiàn)按需加載,降低JS運(yùn)行內(nèi)存峰值。
三、優(yōu)缺點(diǎn)對比
方法一的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
首次頁面顯示成功后,JS運(yùn)行內(nèi)存比較穩(wěn)定,不會(huì)出現(xiàn)后續(xù)滑動(dòng)崩潰的問題,且穩(wěn)定顯示后占用的JS運(yùn)行內(nèi)存較小。
缺點(diǎn):
由于頁面會(huì)一次性全部進(jìn)行解析,在解析比較復(fù)雜的頁面時(shí),會(huì)對JS運(yùn)行內(nèi)存峰值造成比較大的壓力,甚至?xí)?dǎo)致對應(yīng)的頁面無法啟動(dòng)。
方法二的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
在頁面啟動(dòng)時(shí),只對顯示部分進(jìn)行加載,因此可以降低頁面啟動(dòng)時(shí)JS運(yùn)行內(nèi)存。
由于整個(gè)頁面始終只保持對顯示界面的元素進(jìn)行渲染。因此,針對稍復(fù)雜的界面,相較于方法一JS運(yùn)行內(nèi)存峰值更小。
缺點(diǎn):
List組件的內(nèi)容,需要通過$item進(jìn)行訪問, item顯示時(shí)會(huì)創(chuàng)建對應(yīng)的數(shù)據(jù)監(jiān)聽對象來檢測數(shù)據(jù)的變化,比如上述界面中,一個(gè)item會(huì)創(chuàng)建3個(gè)數(shù)據(jù)監(jiān)聽,list中進(jìn)行繪制的item的數(shù)量為5,因此會(huì)創(chuàng)建15個(gè)數(shù)據(jù)監(jiān)聽,從而增加 15 * 200B(單個(gè)字節(jié)) = 3000B的數(shù)據(jù)監(jiān)聽開銷。
隨著list組件向下滑動(dòng),會(huì)增加數(shù)組監(jiān)聽占用的內(nèi)存,從而增加對應(yīng)的JS運(yùn)行內(nèi)存。因此使用方法二,JS運(yùn)行內(nèi)存會(huì)一直上漲,直到最后一個(gè)item渲染。
四、使用建議
針對上述表現(xiàn),我們總結(jié)了如下使用場景供開發(fā)者參考:
圖3 使用建議
總而言之,采用方法二開發(fā)List組件可以降低JS運(yùn)行內(nèi)存峰值,但是會(huì)增加JS運(yùn)行時(shí)內(nèi)存。當(dāng)頁面比較簡單,item數(shù)量低于20個(gè),建議采用方法一。當(dāng)頁面item超過20個(gè),或者頁面占用JS內(nèi)存峰值比較大,建議采用方法二。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)