uni-app 效能最佳化建議

小陳的筆記發表於2022-06-17

執行原理

邏輯層和檢視層分離,且非H5端通訊有折損

uni-app 在非H5端執行時,從架構上分為邏輯層和檢視層兩個部分。邏輯層負責執行業務邏輯,也就是執行js程式碼,檢視層負責頁面渲染。

雖然開發者在一個vue頁面裡寫js和css,但其實,編譯時就已經將它們拆分了。

邏輯層詳解

邏輯層是執行在一個獨立的jscore裡的,它不依賴於本機的webview,所以一方面它沒有瀏覽器相容問題,可以在Android4.4上跑es6程式碼,另一方面,它無法執行window、document、navigator、localstorage等瀏覽器專用的js API。

jscore就是一個標準js引擎,標準js是可以正常執行的,比如if、for、各種字串、日期處理等。js和瀏覽器的區別要注意區分開來。

  • 所謂瀏覽器的js引擎,就是jscore或v8的基礎上新增了一批瀏覽器專用API,比如dom;
  • node.js引擎,則是v8基礎上補充一些電腦專用API,比如本地io;
  • 那麼uni-app的App端和小程式端的js引擎,其實是在jscore上補充了一批手機端常用的JS API,比如掃碼。

檢視層詳解

h5和小程式平臺,以及app-vue,檢視層是webview。而app-nvue的檢視層是基於weex改造的原生檢視。

在iOS上,所有人都只能使用iOS提供的webview。它有一定的瀏覽器相容問題,iOS版本不同,它的表現有細微差異。

Android上小程式大多自帶了一個幾十M的chromium webview,而App端沒辦法帶這麼大體積的三方包,所以App端使用了Android system webview,而系統webview跟隨手機不同而有差異。

所以uni-app的js基本沒有不同手機的相容問題(因為js引擎自帶了),而檢視層的css,在app-vue上會有手機瀏覽器的css相容問題。所以在app-vue的場景中儘量不要使用太新的css語法,除非你不打算支援低端機。

邏輯層和檢視層分離的利與弊

邏輯層和檢視層分離,好處是js運算不卡渲染,最簡單直接的感受就是:窗體動畫穩。

如果開發者使用過App,應該有概念,webview新窗體一邊做進入動畫,一邊自身渲染,很容易卡動畫。而uni-app則無需寫預載程式碼,新窗體渲染快且動畫穩定。

但是兩層分離也帶來一個壞處,這兩層互相通訊,其實是有損耗的。

iOS還好,但Android低端機上,每次通訊都要耗時幾十毫秒。平時看不出來影響,但有幾個場景表現明顯。

  1. 連續高幀率繪製canvas動畫,會發現還不如webview內部繪製流程
  2. 檢視層滾動、跟手操作,不停反饋給邏輯層,js再處理邏輯並通知檢視層做對應更新。此時會發現互動不跟手或卡

不管app-vue/小程式,還是app-nvue,都有相同的問題。

解決這類問題,在webview渲染和原生渲染引用了不同的做法。

  • webview渲染的檢視層

在app-vue和微信小程式上,提供了一種執行於檢視層的專屬js,微信叫做wxs,uni-app也沿用了這個名稱。

微信裡對wxs限制較多,只能實現有限的功能,app端(尤其是v3編譯器下)則沒有限制。

wxs中可以監聽手勢,以uni ui的swiperAction元件為例,手指拖動,側邊的列表選單項要跟手滑出,此時就需要使用wxs才能實現流暢效果。

至於canvas動畫,微信的canvas是原生的,無法運用wxs操作,且一樣有通訊折損,所以繪製動畫的效能不佳。而uni-app的app-vue的canvas是webview的,並且支援wxs操作,開發者可以在普通js中傳遞資料和指令給wxs,在wxs裡繪製動畫,將不再有通訊折損,實現更流暢的效果(app端需v3編譯器)

  • 原生渲染的檢視層

在app-nvue裡,折損一樣存在。包括react native也有這個問題。weex發明了一套bindingx機制,可以在js裡傳一個表示式給原生,由原生解析後根據指令操作檢視層,這個技術在uni-app裡也可以使用。

bindingx作為一種表示式,它的功能不及js強大,但基本的手勢監聽、動畫還是可以實現的,比如uni ui的swiperAction元件在app-nvue下執行時會自動啟用bindingx,以實現流暢跟手。

app-vue和小程式的資料更新,分頁面級和元件級

對於複雜頁面,更新某個區域的資料時,需要把這個區域做成元件,這樣更新資料時就只更新這個元件,否則會整個頁面的資料更新,造成點選延遲卡頓。 這就是自定義元件編譯模式的特點。

比如微博長列表頁面,點選一個點贊圖示,贊數要立即+1,此時這個點贊按鈕一定要做成元件。否則這個+1會引發頁面級所有資料的更新。

app-nvue和h5不存在此問題。造成差異的原因是小程式目前只提供了元件差量更新的機制,不能自動計算所有頁面差量。

最佳化建議

使用自定義元件模式

使用自定義元件模式,在manifest中配置自定義元件模式(HBuilderX1.9起新建專案預設即為自定義元件模式)。

在複雜頁面中,頁面中巢狀大量元件,如果是非自定義元件模式,更新一個元件會導致整個頁面資料更新。而自定義元件模式則可以單獨更新一個元件的資料。

在App端,除了上述好處,自定義元件模式還新增了一個獨立的js引擎,加快啟動速度、減少js阻塞。

之前的非自定義元件模式已經不再推薦,如果你的應用還是非自定義組模式,請儘快升級。

避免使用大圖

頁面中若大量使用大圖資源,會造成頁面切換的卡頓,導致系統記憶體升高,甚至白屏崩潰。

尤其是不要把多張大圖縮小後顯示在一個螢幕內,比如上傳圖片前選了數張幾M體積的照片,然後縮小在一個螢幕中展示多張幾M的大圖,非常容易白屏崩潰。

最佳化資料更新

在 uni-app 中,定義在 data 裡面的資料每次變化時都會通知檢視層重新渲染頁面。 所以如果不是檢視所需要的變數,可以不定義在 data 中,可在外部定義變數或直接掛載在vue例項上,以避免造成資源浪費。

長列表
  • 長列表中如果每個item有一個點贊按鈕,點選後點贊數字+1,此時點贊元件必須是一個單獨引用的元件,才能做到差量資料更新。否則會造成整個列表資料過載。(要求自定義元件模式)
  • 長列表中每個item並不一定需要做成元件,取決於你的業務中是否需要差量更新某一行item的資料,如沒有此類需求則不應該引入大量元件。(點選item後背景變色,屬於css調整,沒有更新data資料和渲染,不涉及這個問題)
  • app端nvue的長列表應該使用list元件,有自動的渲染資源回收機制。vue頁面使用頁面滾動的效能,好於使用scroll-view的區域滾動。uni ui封裝了uList元件,強烈推薦開發者使用,避免自己寫的不好產生效能問題。
  • 如需要左右滑動的長列表,請在HBuilderX新建uni-app專案選新聞模板,那是一個標杆實現。自己用swiper和scroll-view做很容易引發效能問題。
減少一次性渲染的節點數量

頁面初始化時,邏輯層如果一次性向檢視層傳遞很大的資料,使檢視層一次性渲染大量節點,可能造成通訊變慢、頁面切換卡頓,所以建議以區域性更新頁面的方式渲染頁面。如:服務端返回100條資料,可進行分批載入,一次載入50條,500ms 後進行下一次載入。

減少節點巢狀層級

深層巢狀的節點在頁面初始化構建時往往需要更多的記憶體佔用,並且在遍歷節點時也會更慢些,所以建議減少深層的節點巢狀。

避免檢視層和邏輯層頻繁進行通訊
  • 減少 scroll-view 元件的 scroll 事件監聽,當監聽 scroll-view 的滾動事件時,檢視層會頻繁的向邏輯層傳送資料;
  • 監聽 scroll-view 元件的滾動事件時,不要實時的改變 scroll-top/scroll-left 屬性,因為監聽滾動時,檢視層向邏輯層通訊,改變 scroll-top/scroll-left 時,邏輯層又向檢視層通訊,這樣就可能造成通話卡頓。
  • 注意 onPageScroll 的使用,onPageScroll 進行監聽時,檢視層會頻繁的向邏輯層傳送資料;
  • 多使用css動畫,而不是透過js的定時器操作介面做動畫
  • 如果是canvas裡做跟手操作,建議使用web-view元件。web-view裡的頁面沒有邏輯層和檢視層分離的概念,自然也不會有通訊折損。
最佳化頁面切換動畫
  • 頁面初始化時若存在大量圖片或原生元件渲染和大量資料通訊,會發生新頁面渲染和窗體進入動畫搶資源,造成頁面切換卡頓、掉幀。建議延時100ms~300ms渲染圖片或複雜原生元件,分批進行資料通訊,以減少一次性渲染的節點數量。
  • App端動畫效果可以自定義。popin/popout的雙窗體聯動擠壓動畫效果對資源的消耗更大,如果動畫期間頁面裡在執行耗時的js,可能會造成動畫掉幀。此時可以使用消耗資源更小的動畫效果,比如slide-in-right/slide-out-right。
最佳化背景色閃白
  • 如果頁面背景是深色,在vue頁面中可能會發生新窗體剛開始動畫時是灰白色背景,動畫結束時才變為深色背景,造成閃屏。這是因為webview的背景生效太慢的問題。此時需將樣式寫在 App.vue 裡,可以加速頁面樣式渲染速度。App.vue 裡面的樣式是全域性樣式,每次新開頁面會優先載入 App.vue 裡面的樣式,然後載入普通 vue 頁面的樣式。
  • 還可以在pages.json的globalStyle->style->app-plus->background下配置全域性背景色"style": { "app-plus": { "background":"#000000" } }
  • 另外nvue頁面不存在此問題,也可以更改為nvue頁面。
使用nvue代替vue

在 App 端 uni-app 的 nvue 頁面可是基於 weex 定製的原生渲染引擎,實現了頁面原生渲染能力、提高了頁面流暢性。若對頁面效能要求較高可以使用此方式開發,詳見:nvue。

最佳化啟動速度
  • 工程程式碼越多,包括背景圖和本地字型檔案越大,對App的啟動速度有影響,應注意控制體積。元件引用的前景圖不影響效能。
  • App端的 splash 關閉有白屏檢測機制,如果首頁一直白屏或首頁本身就是一個空的中轉頁面,可能會造成 splash 10秒才關閉,可參考此文解決
  • App端使用自定義元件模式時啟動速度更快,首頁為nvue頁面時啟動速度更快
  • App設定為純nvue專案(manifest裡設定app-plus下的renderer:"native"),這種專案的啟動速度更快,2秒即可完成啟動。因為它整個應用都使用原生渲染,不載入基於webview的那套框架。
最佳化包體積
  • uni-app發行到小程式時,自帶引擎只有幾十K,主要是一個定製過的vue.js核心庫。如果使用了es6轉es5、css對齊的功能,可能會增大程式碼體積,可以配置這些編譯功能是否開啟。
  • uni-app的H5端,自帶了vue.js、vue-router及部分es6 polyfill庫,這部分的體積gzip後只有92k,和web開發使用vue基本一致。而內建元件ui庫(如picker、switch等)、小程式的對齊js api等,相當於一個完善的大型ui庫。但大多數應用不會用到所有內建元件和API。由此uni-app提供了搖樹最佳化機制,未搖樹最佳化前的uni-app整體包體積約500k,伺服器部署gzip後162k。開啟搖樹最佳化需在manifest配置,詳情。
  • uni-app的App端,因為自帶了一個獨立v8引擎和小程式框架,所以比HTML5Plus或mui等普通hybrid的App引擎體積要大。Android基礎引擎約15M。App還提供了擴充套件模組,比如地圖、藍芽等,打包時如不需要這些模組,可以裁剪掉,以縮小發行包體積。在 manifest.json-App模組許可權 裡可以選擇。
  • App端支援如果選擇純nvue專案(manifest裡設定app-plus下的renderer:"native"),包體積可以進一步減少2M左右。
  • uni-app的App端預設包含arm32和x86兩個cpu的支援so庫。這會增大包體積。如果你在意體積控制,可以在manifest裡去掉x86 cpu的支援(manifest視覺化介面-App其他設定裡選擇cpu),這可以減少包體積到9M。但代價是不支援intel的cpu了。一般手機都是arm的,僅個別少見的Android pad使用x86 cpu。另外as的模擬器裡如果選擇x86時也無法執行這種apk。




來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70018483/viewspace-2901074/,如需轉載,請註明出處,否則將追究法律責任。

相關文章