構(gòu)建高性能 React Native 跨端應(yīng)用—圖片與內(nèi)存
一 前言
在構(gòu)建高性能 React Native 跨端應(yīng)用—引擎與渲染章節(jié)中,我們從引擎與渲染角度介紹了 React Native 的優(yōu)化手段,本文我們繼續(xù)從圖片和內(nèi)存角度繼續(xù)討論一下如何構(gòu)建高性能的 React Native 應(yīng)用。
二 圖像層面
在瀏覽器構(gòu)建的 web 中開發(fā)者可能不用花費(fèi)太多精力關(guān)注圖像上,但是在移動(dòng)應(yīng)用中,對于圖像的關(guān)注顯得非常重要。因?yàn)樵?RN 應(yīng)用中,無論是圖片還是動(dòng)圖,或者是視頻都是非常耗內(nèi)存的,內(nèi)存的暴漲就很容易造成應(yīng)用的崩潰。
圖片合理應(yīng)用
圖片的處理,占 RN 性能優(yōu)化的大頭,在現(xiàn)在的移動(dòng)端應(yīng)用中,有很多應(yīng)用大量圖片的場景,加載圖片的過程實(shí)際是很復(fù)雜的,并且圖片本身的大小,也不是最后加載到內(nèi)存中的大小,也就是說最后落實(shí)在內(nèi)存里面的大小,會(huì)大于圖片本身的大小。
圖片的處理在不同平臺上表現(xiàn)也不一致,在 iOS 平臺上對于圖像的加載,加密,到最后的展現(xiàn),表現(xiàn)還算比較好。但是在安卓平臺,就時(shí)常會(huì)出現(xiàn)幺蛾子。
筆者在開發(fā) RN 應(yīng)用中,就遇到了這樣的場景:我們 RN 只運(yùn)行在安卓端,一個(gè) RN 頁面會(huì)加載大量的圖片,剛開始我們沒有對圖片進(jìn)行任何處理,只是圖片的寬度和高度是寫死的,那么造成的現(xiàn)象是,所有的圖片都展現(xiàn)不出來,并且圖片是黑的,接下來就是安卓程序直接崩潰。
后來經(jīng)過排查我們發(fā)現(xiàn),原來我們給圖片的容器特別小,但是圖片資源卻非常大,由于為了在小容器中呈現(xiàn)大的圖片,就比如說一個(gè) 100 * 100 圖片容器,加載一個(gè) 1000 * 1000 圖片,安卓底層需要對圖片源數(shù)據(jù)進(jìn)行算法壓縮,此時(shí)就會(huì)讓內(nèi)存暴漲,幀率直接降為個(gè)位數(shù),導(dǎo)致黑屏,閃退的情況。筆者還把這種小容器加載大圖片的情況,叫做小馬拉大車。
那么如何解決這個(gè)問題呢? RN 中的 Image 組件有個(gè) resizeMethod 屬性,就是解決 Android 圖片內(nèi)存暴漲的問題。當(dāng)圖片實(shí)際尺寸和容器樣式尺寸不一致時(shí),決定以怎樣的策略來調(diào)整圖片的尺寸。
<Image resizeMethod="resize" source={{ uri: imageUrl }} />
resizeMethod 屬性有三個(gè)可選的值,默認(rèn)為 auto .
resize:小容器加載大圖的場景就應(yīng)該用這個(gè)屬性。原理是在圖片解碼之前,會(huì)用算法對其在內(nèi)存中的數(shù)據(jù)進(jìn)行修改,一般圖片大小大概會(huì)縮減為原圖的 1/8。 scale:不改變圖片字節(jié)大小,通過縮放來修改圖片寬高。因?yàn)橛杏布铀?,所以加載速度會(huì)更快一些。
auto:使用啟發(fā)式算法來在resize和scale中自動(dòng)決定,,如果是本地圖片,就會(huì)用 resize,其他的一般都是 scale 屬性,由于項(xiàng)目運(yùn)用的是網(wǎng)絡(luò)圖片,所以就按照 scale 處理邏輯。
實(shí)際最佳的方案就是,適當(dāng)?shù)拇笮〉膱D片容器,加載適當(dāng)?shù)膱D片。但是對于一些圖片資源的大小是未知的,我們不能直接通過設(shè)置寬和高的方式草率的設(shè)置圖片容器大小,解決方案就是可以通過 api 的方式獲取遠(yuǎn)程圖片的大小。如下:
import { Image } from 'react-native'
/* 使用 */
Image.getSize(imageUrl,(width,height)=>{
console.log('寬度:',width,'高度:',height)
})
當(dāng)然客戶端也可以把圖片壓縮的操作交給服務(wù)端去做,目前很多大公司都有自己的內(nèi)建圖床和 CDN 服務(wù),會(huì)提供一些自定制圖片的功能,在請求圖片資源的時(shí)候,就把圖片的寬和高拼接到 url 中,這樣服務(wù)器接受到圖片請求,會(huì)根據(jù)路徑獲取 width 和 height,然后自行的對圖片進(jìn)行壓縮。返回給客戶端的就已經(jīng)是處理好的能夠適配圖片容器大小的圖片了。
圖片管理優(yōu)化
上面介紹了圖片的合理使用,接下來我們看一下圖片的管理優(yōu)化,在 RN 中有多種多樣的類型的圖片,比如 png/jpg/base64/gif ,對于 gif 在安卓 build.gradle 中需要添加相關(guān)依賴。對于一些動(dòng)圖的處理,比如 svg 和 svga ,RN 也提供了相關(guān)的生態(tài)去處理這些圖像。
對圖片的管理可以通過不同的場景,運(yùn)用更為合理的方案。比如對于一些大量 gif 圖片的場景,內(nèi)存就是一個(gè)棘手問題,圖片的管理工具就需要均衡好內(nèi)存緩存和磁盤緩存的策略,一般都會(huì)采用三級緩存策略。
對于一些網(wǎng)絡(luò)加載的圖片,在一些網(wǎng)絡(luò)差或者特殊網(wǎng)絡(luò)的情況下,可以出現(xiàn)加載慢,丟包的現(xiàn)象,這樣就會(huì)導(dǎo)致圖片一致加載失敗。慶幸的是,還有專門的圖片管理庫來來解決這個(gè)問題。那就是 react-native-fast-image。
react-native-fast-image 這個(gè)庫比較受歡迎的,它對圖片的加載和內(nèi)存優(yōu)化上都有著不錯(cuò)的表現(xiàn)。這個(gè)庫在 iOS 和安卓平臺上,底層用原理也各不相同。
三 內(nèi)存層面
清除資源
對于清楚資源,談不上具體的主流優(yōu)化手段,確切的說,應(yīng)該是一個(gè)值得關(guān)注的細(xì)節(jié)。
比如當(dāng) A 頁面中有視頻播放的模塊,而 B 頁面是 A 的二級頁面,在融合模式下,進(jìn)入 A 頁面之后會(huì)開始播放視頻流,但是當(dāng)從 A 頁面進(jìn)入到 B 頁面之后,本質(zhì)上 A 頁面并沒有被回收,但是這個(gè)時(shí)候,還在加載著視頻資源。那么這樣下去,會(huì)讓內(nèi)存越來越大。
那么如何解決這個(gè)問題呢? 當(dāng) A 跳轉(zhuǎn)到 B 頁面之后,應(yīng)該停止 A 頁面加載資源,或者清空視頻資源,讓內(nèi)存維護(hù)一個(gè)健康的水平。
對于一些超多 gif 圖片的頁面,并還有列表加載功能,這樣在向下加載數(shù)據(jù)的過程中,會(huì)渲染更多的 gif 組件,這樣就會(huì)讓內(nèi)存越來越大,并且不容易下來,或者一些低端的機(jī)型,根本無法渲染太多的 gif 圖片,那么此時(shí)應(yīng)該如何解決呢?
這個(gè)時(shí)候可以做一個(gè)優(yōu)化,就是只有在視圖范圍內(nèi)的元素才渲染真正的 gif 圖片,而其他看不見的直接渲染圖片或者是占位圖。如下所示:
圖片
WechatIMG2339.png
清除狀態(tài)
對于一些全局的狀態(tài),比如存在 Redux 中的數(shù)據(jù)源,或者是全局綁定的監(jiān)聽事件,setTimeout 延時(shí)器
四 總結(jié)
本文從圖像與內(nèi)存兩個(gè)方面介紹了 RN 優(yōu)化手段,希望這篇文章的能給 React Native 開發(fā)同學(xué)一個(gè)性能優(yōu)化上啟發(fā)。
參考
- React Native 性能優(yōu)化指南
- 大前端跨端開發(fā)指南