JS UI框架下,List組件運行時的內存優化
每種編程語言都有它的內存管理機制,不同設備上可用內存不同,分配給JS引擎可用的內存范圍也不同。例如運行內存在128MB以下的輕量設備,對應JS引擎的可用內存范圍為48 64KB。本文也將以此類設備為例進行分析。
當整個頁面渲染比較復雜時,JS運行內存峰值就可能會超過JS引擎分配到的最大可用內存,導致頁面無法渲染。
List組件是JS UI框架下最基本的容器組件之一,提供了一系列相同寬度的列表項。在應用開發過程中,經常會使用List容器組件來呈現大量的數據。所以,在List組件應用的開發過程中,開發者應充分考慮內存優化問題。
本期,我們將通過List組件開發一個通訊錄頁面,并采用list+for的方案對整個頁面進行優化,達到減小JS運行內存的目的。
一、代碼實現
如下所示,是一張簡單的通訊錄頁面,包含了姓名、電話及對應圖片。下面將通過兩種實現方式來對比代碼性能。

圖1 簡單的通訊錄頁面
方法一:直接書寫對應的組件頁面
使用HML直接撰寫整個組件頁面的內容,代碼如下:
- <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指令來書寫對應的組件頁面
針對方法一中的實現,采用for指令來改進,使對應頁面更簡潔,對應修改后代碼如下:
- <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>
對應的for指令的渲染數組代碼如下:
- 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數量,分別測試了以上兩種實現方式的JS運行性能,JS運行內存與JS運行內存峰值如下圖所示:

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





























