uni-app 性能優(yōu)化建議
1. 前言
初期學(xué)習(xí)開(kāi)發(fā) uni-app 項(xiàng)目,我們一般不會(huì)在意項(xiàng)目的性能問(wèn)題,項(xiàng)目功能較少或者數(shù)據(jù)較少的時(shí)候,不會(huì)對(duì)項(xiàng)目性能的重要性有很大的感受。
但如果開(kāi)發(fā)大型項(xiàng)目,或者數(shù)據(jù)量比較大的時(shí)候,項(xiàng)目性能的問(wèn)題就顯得尤為重要。一種需求的不同實(shí)現(xiàn)方式,頁(yè)面的加載速度會(huì)差幾倍甚至上百倍。
所以我們?cè)陂_(kāi)發(fā)項(xiàng)目之前,最好先了解一下項(xiàng)目性能優(yōu)化的幾種方式,在項(xiàng)目初期就打好基礎(chǔ),以免后期維護(hù)過(guò)于麻煩。
這節(jié)課我們主要講解一些性能優(yōu)化的小建議,在項(xiàng)目開(kāi)發(fā)之前最好看一看,知道開(kāi)發(fā)的重點(diǎn)需要注意哪些地方。
2. uni-app 運(yùn)行原理
uni-app 項(xiàng)目的視圖層和邏輯層是分離開(kāi)的,雖然我們?cè)陂_(kāi)發(fā)項(xiàng)目過(guò)程中,將 html、js 代碼都寫(xiě)在同一個(gè)文件中,但是實(shí)際運(yùn)行的時(shí)候是分離開(kāi)的。
視圖層負(fù)責(zé)進(jìn)行頁(yè)面渲染,也就是用戶(hù)能看到的頁(yè)面,用來(lái)展示數(shù)據(jù)的,包括頁(yè)面結(jié)構(gòu)代碼 <template>
部分、頁(yè)面樣式代碼 <style>
部分。
邏輯層負(fù)責(zé)執(zhí)行后端的業(yè)務(wù)邏輯,用戶(hù)看不到這部分的邏輯,用來(lái)處理數(shù)據(jù)的,包括頁(yè)面邏輯代碼 <script>
部分,以及其他 .js 文件。
視圖層和邏輯層分離,減少了項(xiàng)目的耦合程度,并且邏輯層的運(yùn)算不會(huì)影響到視圖層的渲染,窗體動(dòng)畫(huà)會(huì)比較穩(wěn)。而兩層分離也有一個(gè)缺點(diǎn),就是這兩層互相通信會(huì)有損耗。
我們下面進(jìn)行的性能優(yōu)化,都是基于uni-app 運(yùn)行原理來(lái)操作的,下面來(lái)具體看一下。
3. 性能優(yōu)化建議
3.1 長(zhǎng)列表優(yōu)化
我們開(kāi)發(fā)項(xiàng)目時(shí),我們經(jīng)常會(huì)循環(huán)長(zhǎng)列表,將長(zhǎng)列表中的數(shù)據(jù)逐一展示在項(xiàng)目中,但是你開(kāi)發(fā)過(guò)程中,有沒(méi)有出現(xiàn)過(guò)一旦數(shù)據(jù)過(guò)多項(xiàng)目加載會(huì)變得很慢的問(wèn)題呢?
長(zhǎng)列表的應(yīng)用有許多需要我們注意的地方,或許你在開(kāi)發(fā)過(guò)程中沒(méi)有注意下面幾個(gè)問(wèn)題造成長(zhǎng)列表加載過(guò)慢,我們來(lái)具體看看。
3.1.1 長(zhǎng)列表差量數(shù)據(jù)更新
如果你長(zhǎng)列表的數(shù)據(jù)中,每個(gè)列表都有可能差量更新,則需要將長(zhǎng)列表中的每個(gè) item 都做成一個(gè)組件。不然其中每個(gè) item 更新,都會(huì)造成整個(gè)長(zhǎng)列表的重新加載,嚴(yán)重良妃系統(tǒng)資源,我們來(lái)舉個(gè)例子。
比如我們加載博文的100條評(píng)論,每條評(píng)論都有一個(gè)點(diǎn)贊功能。
如果每條評(píng)論沒(méi)有做成單獨(dú)的組件,用戶(hù)每次給其中一條評(píng)論點(diǎn)贊一次,系統(tǒng)都會(huì)重新加載這100條評(píng)論。
如果每條評(píng)論都做成了單獨(dú)的組件,用戶(hù)給其中一條評(píng)論點(diǎn)贊,系統(tǒng)只會(huì)重新加載點(diǎn)贊的這一條評(píng)論,其他評(píng)論不受影響,合理利用系統(tǒng)資源。
實(shí)例:
<template>
<view>
<view class="thumb" v-for="item in testdata">
<view>{{item}}</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
testdata: ["評(píng)價(jià)1","評(píng)價(jià)2","評(píng)價(jià)3","評(píng)價(jià)4","評(píng)價(jià)5"]
}
}
}
</script>
<style>
.thumb{
text-align: center;
margin-top: 20px;
}
</style>
// thumbitem.vue 將每個(gè) item 包裝成組件
<template>
<view>
<!-- 顯示 item 信息 -->
<view>{{item}}</view>
<!-- 點(diǎn)贊按鈕,點(diǎn)擊觸發(fā) thumb 方法 -->
<button @click="thumb(index)">點(diǎn)贊數(shù):{{thumbs}}</button>
</view>
</template>
<script>
export default {
props:['item'],
data() {
return {
thumbs:0
};
},
methods: {
// 每次觸發(fā) thumb 方法,點(diǎn)贊數(shù) thumbs 變量就加 1
thumb(){
this.thumbs += 1
}
}
}
</script>
3.1.2 長(zhǎng)列表無(wú)差量數(shù)據(jù)更新
如果長(zhǎng)列表中,每個(gè) item 不會(huì)單獨(dú)去更新,那我們就沒(méi)有必要去將每個(gè) item 都做成一個(gè)組件了,直接循環(huán)長(zhǎng)列表顯示 item 就可以。
實(shí)例:
// index.vue 循環(huán)加載長(zhǎng)列表
<template>
<view>
<view class="thumb" v-for="item in testdata">
<ThumbItem :item='item'></ThumbItem>
</view>
</view>
</template>
<script>
Import ThumbItem from "components/thumbitem/thumbitem.vue"
export default {
components: {
ThumbItem
},
data() {
return {
testdata: ["評(píng)價(jià)1","評(píng)價(jià)2","評(píng)價(jià)3","評(píng)價(jià)4","評(píng)價(jià)5"]
}
}
}
</script>
<style>
.thumb{
text-align: center;
margin-top: 20px;
}
</style>
我們?cè)陂_(kāi)發(fā)過(guò)程中,不需要每次都將長(zhǎng)列表的 item 包裝成組件,每個(gè) item 需要差量數(shù)據(jù)更新的時(shí)候,才需要包裝成組件。
組件在頁(yè)面初始化時(shí)會(huì)占用更多的內(nèi)存,并且遍歷節(jié)點(diǎn)也會(huì)更慢,每個(gè)組件渲染時(shí)都會(huì)觸發(fā)一次通信,太多組件就會(huì)阻塞通信。所以我們要將好刀用在刀刃上,不分情況到處使用反而會(huì)適得其反。深層節(jié)點(diǎn)的嵌套也是同樣的道理,我們開(kāi)發(fā)的時(shí)候要注意盡量避免深層節(jié)點(diǎn)嵌套。
在實(shí)際項(xiàng)目開(kāi)發(fā)中,長(zhǎng)列表一般是由邏輯層處理后返回的,數(shù)據(jù)是變化的,如果長(zhǎng)列表中的數(shù)據(jù)需要展示在頁(yè)面上,那么我們就將長(zhǎng)列表定義在 data 中,如果變量不需要展示在視圖中,我們盡量將變量定于在 data 外部。
因?yàn)閐ata 中的數(shù)據(jù)每次發(fā)生變化,視圖層都要重新渲染頁(yè)面。這樣做可以盡量避免資源的浪費(fèi),這條建議同樣也適用于其他變量。
3.2 減少一次性加載的節(jié)點(diǎn)數(shù)量
頁(yè)面初始化時(shí),邏輯層如果一次性向視圖層傳遞很多數(shù)據(jù),不僅會(huì)造成邏輯層和視圖層之間的通訊變化,由于視圖層一次渲染的數(shù)據(jù)過(guò)多,還會(huì)造成頁(yè)面的卡頓。
所以如果數(shù)據(jù)過(guò)多,建議進(jìn)行分頁(yè)加載。比如:服務(wù)端要返回1000條數(shù)據(jù),可以一次加載100條,500ms 后再進(jìn)行下一次加載。
3.3 盡量避免兩層頻繁通信
上面我們提到過(guò),邏輯層與視圖層分離會(huì)造成一個(gè)缺點(diǎn),就是兩層之間通訊的損耗,如果兩層之間頻繁通信,可能會(huì)造成通訊阻塞,影響項(xiàng)目的正常運(yùn)行,如果盡量避免兩層之間的頻繁通信呢?主要有以下幾點(diǎn):
3.3.1 scroll-view 組件的使用
- 使用scroll-view 組件時(shí),要盡量減少監(jiān)聽(tīng)滾動(dòng)事件,因?yàn)樵诒O(jiān)聽(tīng) scroll-view 的滾動(dòng)事件時(shí),視圖層會(huì)頻繁的向邏輯層發(fā)送數(shù)據(jù);
- 不要實(shí)時(shí)的改變 scroll-top、scroll-left 屬性,可能造成通訊卡頓;
- 如果要實(shí)現(xiàn)下拉刷新的功能,建議使用頁(yè)面的滾動(dòng),而不是 scroll-view;
- 不要在 scroll-view 組件中放長(zhǎng)列表,會(huì)導(dǎo)致性能問(wèn)題。
3.3.2 圖片、動(dòng)畫(huà)加載問(wèn)題
- 盡量使用壓縮圖片;
- 盡量使用css 動(dòng)畫(huà),而不是通過(guò) js 的定時(shí)器操作界面做動(dòng)畫(huà);
- 如果項(xiàng)目中不可避免的需要加載大量的動(dòng)畫(huà)和圖片,建議加載時(shí)進(jìn)行延時(shí)處理,分批進(jìn)行大量數(shù)據(jù)通訊,提高頁(yè)面的流暢性。否則會(huì)造成頁(yè)面切換卡頓、掉幀等問(wèn)題;
- App-nvue 和 H5 項(xiàng)目總,還支持頁(yè)面預(yù)載,使用API uni.preloadPage,開(kāi)發(fā)時(shí)可以使用提高用戶(hù)的操作體驗(yàn)。
3.4 使用nvue代替vue
因?yàn)?nvue 頁(yè)面開(kāi)發(fā)限制比較多,我們前面建議大家盡量不要使用 nvue 來(lái)開(kāi)發(fā)項(xiàng)目,但是如果更加看重項(xiàng)目性能問(wèn)題,可以使用nvue 來(lái)代替 vue。
因?yàn)?nvue 是基于 weex 的原生渲染,可以有效提高頁(yè)面的流暢程度。
如果對(duì)項(xiàng)目啟動(dòng)速度有更高的要求,如果項(xiàng)目支持的話(huà),甚至可以將項(xiàng)目設(shè)置為純 nvue 項(xiàng)目,這樣整個(gè)應(yīng)用都使用原生渲染,不加載 webview 框架,這樣項(xiàng)目的啟動(dòng)速度會(huì)更快。
4. 小結(jié)
性能優(yōu)化問(wèn)題,在項(xiàng)目開(kāi)發(fā)過(guò)程中,會(huì)變得越來(lái)越重要,最好在開(kāi)發(fā)之前就了解各種優(yōu)化性能的小技巧,這樣后期開(kāi)發(fā)也會(huì)更加得心應(yīng)手一些。
本小節(jié)可以先瀏覽一遍項(xiàng)目開(kāi)發(fā)中性能優(yōu)化的這幾個(gè)建議,大體記得哪些方面需要注意。等項(xiàng)目用到的時(shí)候,再來(lái)結(jié)合課程應(yīng)用到實(shí)際項(xiàng)目中,這樣也會(huì)理解的更加深刻。