你可能不知道的mpvue性能優化技巧(轉)

https://www.jianshu.com/p/6903d3158390
最近一直在折騰mpvue寫的微信小程序的性能優化,分享下實戰的過程。javascript

先上個優化先後的圖:css

 
image


能夠看到打包後的代碼量從813KB減小到387KB,Audits體驗評分從BA,效果仍是比較明顯的。其實這個指標說明不了什麼,並且輕易就能夠作到,更重要的是優化小程序運行過程當中的卡頓感,請耐心往下看。html

 

常規優化

常規的Web端優化方法在小程序中也是適用的,並且不可忽視。vue

1、壓縮圖片

這一步最簡單,可是容易被忽視。在tiny上在線壓縮,而後下載替換便可。java

 

 
image


我這項目的壓縮率高達72%,能夠說打包後的代碼從813KB降到387KB大部分都是歸功於壓縮圖片了。git

 

2、移除無用的庫

我以前在項目中使用了Vant Weapp,在static目錄下引入了整個庫,但實際上我只使用了button,field,dialog等幾個組件,實在是不必。github

因此乾脆移除掉了,微信小程序自身提供的buttonwx.showModal等一些組件基本能夠知足需求,本身手寫一下樣式也不用花什麼時間。vuex

在這裏建議你們,在微信小程序中,儘可能避免使用過多的依賴庫。小程序

不要貪圖方便而引入一些比較大的庫,小程序不一樣於Web,限制比較多,能本身寫一下就儘可能本身寫一下吧。後端

小程序的優化

我們首先得看一下官方優化建議,大可能是圍繞這個建議去作。

1、開啓Vue.config._mpTrace = true

這個是mpvue性能優化的一個黑科技啊,可能大多數同窗都不知道這個,我在官方文檔都沒有搜到到這個配置,我真的是服了。

我能找到這個配置也是Google機緣巧合下看到的,出處:mpvue重要更新,頁面更新機制進行全面升級
具體作法是在/src/main.js添加Vue.config._mpTrace = true,如:

Vue.config._mpTrace = true
Vue.config.productionTip = false
App.mpType = 'app'

添加了Vue.config._mpTrace屬性,這樣就能夠看到console裏會打印每500ms更新的數據量。如圖:

 
image


若是數據更新量很大,會明顯感受小程序運行卡頓,反之就流暢。所以咱們能夠根據這個指標,逐步找出性能瓶頸並解決掉。

 

2、精簡data

1. 過濾api返回的冗餘數據

後端的api多是須要同時爲iOS,Android,H5等提供服務的,每每會有些冗餘的數據小程序是用不到的。好比api返回的一個文章列表數據有不少字段:

this.articleList = [ { articleId: 1, desc: 'xxxxxx', author: 'fengxianqi', time: 'xxx', comments: [ { userId: 2, conent: 'xxx' } ] }, { articleId: 2 // ... }, // ... ] 

假設咱們在小程序中只須要用到列表中的部分字段,若是不對數據作處理,將整個articleListsetData進去,是不明智的。

小程序官方文檔: 單次設置的數據不能超過1024kB,請儘可能避免一次設置過多的數據

能夠看出,內存是很寶貴的,當articleList數據量很是大超過1M時,某些機型就會爆掉(我在iOS中遇到過不少次)。

所以,須要將接口返回的數據剔除掉不須要的,再setData,回到咱們上面的articleList例子,假設咱們只須要用articleIdauthor這兩個字段,能夠這樣:

import { getArticleList } from '@/api/article' export default { data () { return { articleList: [] } } methods: { getList () { getArticleList().then(res => { let rawList = res.list this.articleList = this.simplifyArticleList(rawList) }) }, simplifyArticleList (list) { return list.map(item => { return { articleId: item.articleId, author: item.author // 須要哪些字段就加上哪些字段 } }) } } } 

這裏咱們將返回的數據經過simplifyArticleList來精簡數據,此時過濾後的articleList中的數據相似:

[ {articleId: 1, author: 'fengxianqi'}, {articleId: 2, author: 'others'} // ... ] 

固然,若是你的需求中是全部數據都要用到(或者大部分數據),就不必作一層精簡了,收益不大。畢竟精簡數據的函數中具體的字段,是會增長維護成本的。

PS: 在我我的的實際操做中,作數據過濾雖然增長了維護的成本,但通常收益都很大,因次這個方法比較推薦。

2. data()中只放須要的數據

import xx from 'xx.js' export default { data () { return { xx, otherXX: '2' } } } 

有些同窗可能會習慣將import的東西都先放進data中,再在methods中使用,在小程序中多是個很差的習慣。

由於經過Vue.config._mpTrace = true在更新某個數據時,我對比放進data和不放進data中的兩種狀況會有差異。

因此我猜想多是data是會一塊兒更新的,好比只是想更新otherXX時,會同時將xx也一塊兒合起來setData了。

3. 靜態圖片放進static

這個問題和上面的問題實際上是同樣的,有時候咱們會經過import的方式引入,好比這樣:

<template> <img :src="UserIcon"> </template> <script> import UserIcon from '@/assets/images/user_icon.png' export default { data () { return { UserIcon } } } </script> 

這樣會致使打包後的代碼,圖片是base64形式(很長的一段字符串)存放在data中,不利於精簡data。同時當該組件多個地方使用時,每一個組件實例都會攜帶這一段很長的base64代碼,進一步致使數據的冗餘。

所以,建議將靜態圖片放到static目錄下,這樣引用:

<template> <img src="/static/images/user_icon.png"> </template> 

代碼也更簡潔清爽。

看一下作了上面操做的先後對比圖,使用體驗上也流暢了不少。

 

 
image

3、swiper優化

小程序自身提供的swiper組件性能上不是很好,使用時要注意。參考着兩個思路:

在我使用時,因爲需求緣由,動態刪掉swiper-item的思路不可行(手滑時會形成抖動)。所以只能做罷。但仍然能夠優化一下:

  • 將未顯示的swiper-item中的圖片用v-if隱藏到,當判斷到current時才顯示,防止大量圖片的渲染致使的性能問題。

4、vuex使用注意事項

我以前寫過的一篇mpvue開發音頻類小程序踩坑和建議裏面有講如何在小程序中使用vuex。但遇到了個比較嚴重的性能問題。

1. 問題描述

我開發的是一個音頻類的小程序,因此須要將播放列表playList,當前索引currentIndex和當前時長currentTime放進state.js中:

const state = { currentIndex: 0, // playList當前索引 currentTime: 0, // 當前播放的進度 playList: [], // {title: '', url: '', singer: ''} } 

每次用戶點擊播放音頻時,都會先加載音頻的播放列表playList,而後播放時更新當前時長currentTime,發現有時候播音頻時整個小程序很是卡頓。

注意到,音頻需每秒就得更新一次currentTime,即每秒就作一次setData操做,稍微有些卡頓是能夠理解的。但我發現是播放列表數據比較多時會特別卡,好比playList的長度是100條以上時。

2. 問題緣由

我開啓Vue.config._mpTrace = true後發現一個規律:

palyList數據量小時,console顯示形成的數據量更新數值比較小;當playList比較大時,console顯示形成的數據量更新數值比較大。

PS: 我曾嘗試將playList數據量增長到200條,每500ms的數據量更新達到800KB左右。

到這裏基本能夠肯定一個事實就是:更新state中的任何一個字段,將致使整個state全量一塊兒setData。在我這裏的例子,雖然我每次只是更新currentTime這個字段的值,但依然致使將state中的其餘字段如playList,currentIndex都一塊兒作了一次setData操做。

3. 解決問題

有兩個思路:

  • 精簡state中保存的數據,即限制playList的數據不能太多,可將一些數據暫存在storage
  • vuex採用Module的寫法能改善這個問題,雖然使用時命名空間形成必定的麻煩。vuex傳送門

通常狀況下,推薦使用後者。我在項目中嘗試使用了前者,一樣能達到很好的效果,請繼續看下面的分享。

5、善用storage

1.爲何說要善用storage

因爲小程序的內存很是寶貴,佔用內存過大會很是卡頓,所以最好儘量少的將數據放到內存中,即vuex存的數據要儘量少。而小程序的storage支持單個 key容許存儲的最大數據長度爲 1MB,全部數據存儲上限爲 10MB

因此能夠將一些相對取用不頻繁的數據放進storage中,須要時再將這些數據放進內存,從而緩解內存的緊張,有點相似Windows中虛擬內存的概念。

2.storage換內存的實例

這個例子講的會有點囉嗦,真正能用到的朋友能夠詳細看下。

上面講到playList數據量太多,播放一條音頻時其實只須要最多保證3條數據在內存中便可,即上一首播放中的下一首,咱們能夠將多餘的播放列表存放在storage中。

PS: 爲了保證更平滑地連續切換下一首,咱們能夠稍微保存多幾條,好比我這裏選擇保存5條數據在vuex中,播放時始終保證當前播放的音頻先後都有兩條數據。

// 首次播放背景音頻的方法 async function playAudio (audioId) { // 拿到播放列表,此時的playList最多隻有5條數據。getPlayList方法看下面 const playList = await getPlayList(audioId) // 當前音頻在vuex中的currentIndex const currentIndex = playList.findIndex(item => item.audioId === audioId) // 播放背景音頻 this.audio = wx.getBackgroundAudioManager() this.audio.title = playList[currentIndex].title this.audio.src = playList[currentIndex].url // 經過mapActions將播放列表和currentIndex更新到vuex中 this.updateCurrentIndex(index) this.updatePlayList(playList) // updateCurrentIndex和updatePlayList是vuex寫好的方法 } // 播放音頻時獲取播放列表的方法,將全部數據存在storage,而後返回當前音頻的先後2條數據,保證最多5條數據 import { loadPlayList } from '@/api/audio' async function getPlayList (courseId, currentAudioId) { // 從api中請求獲得播放列表 // loadPlayList是api的方法, courseId是獲取列表的參數,表示當前課程下的播放列表 let rawList = await loadPlayList(courseId) // simplifyPlayList過濾掉一些字段 const list = this.simplifyPlayList(rawList) // 將列表存到storage中 wx.setStorage({ key: 'playList', data: list }) return subPlayList(list, currentAudioId) } 

重點是subPlayList方法,這個方法保證了拿到的播放列表是最多5條數據。

function subPlayList(playList, currentAudioId) { let tempArr = [...playList] const count = 5 // 保持vuex中最多5條數據 const middle = parseInt(count / 2) // 中點的索引 const len = tempArr.length // 若是整個原始的播放列表原本就少於5條數據,說明不須要裁剪,直接返回 if (len <= count) { return tempArr } // 找到當前要播放的音頻的所在位置 const index = tempArr.findIndex(item => item.audioId === currentAudioId) // 截取當前音頻的先後兩條數據 tempArr = tempArr.splice(Math.max(0, Math.min(len - count, index - middle)), count) return tempArr } 

tempArr.splice(Math.max(0, index - middle), count)可能有些同窗比較難理解,須要仔細琢磨一下。假設playList有10條數據:

  • 當前音頻是列表中的第1條(索引是0),截取前5個:playList.splice(0, 5),此時currentAudio在這5個數據的索引是0,沒有上一首,有4個下一首
  • 當前音頻是列表中的第2條(索引是1),截取前5個:playList.splice(0, 5),此時currentAudio在這5個數據的索引是1,有1個上一首,3個下一首
  • 當前音頻是列表中的第3條(索引是2),截取前5個:playList.splice(0, 5),此時currentAudio在這5個數據的索引是2,有2個上一首,2個下一首
  • 當前音頻是列表中的第4條(索引是3),截取第1到6個:playList.splice(1, 5)
    ,此時currentAudio在這5個數據的索引是2,有2個上一首,2個下一首
  • 當前音頻是列表中的第5條(索引是4),截取第2到7個:playList.splice(2, 5),此時currentAudio在這5個數據的索引是2,有2個上一首,2個下一首
  • ...
  • 當前音頻是列表中的第9條(索引是8),截取後5個:playList.splice(4, 5),此時currentAudio在這5個數據的索引是3,有3個上一首,1個下一首
  • 當前音頻是列表中的最後1條(索引是9),截取後的5個:playList.splice(4, 5),此時currentAudio在這5個數據的索引是4,有4個上一首,沒有下一首

有點囉嗦,感興趣的同窗仔細琢磨下,不管當前音頻在哪,都始終保證了拿到當前音頻先後的最多5條數據。

接下來就是維護播放上一首或下一首時保證當前vuex中的playList始終是包含當前音頻的先後2條。

播放下一首
function playNextAudio() { const nextIndex = this.currentIndex + 1 if (nextIndex < this.playList.length) { // 沒有超出數組長度,說明在vuex的列表中,能夠直接播放 this.audio = wx.getBackgroundAudioManager() this.audio.src = this.playList[nextIndex].url this.audio.title = this.playList[nextIndex].title this.updateCurrentIndex(nextIndex) // 當判斷到已經到vuex的playList的邊界了,從新從storage中拿數據補充到playList if (nextIndex === this.playList.length - 1 || nextIndex === 0) { // 拿到只有當前音頻先後最多5條數據的列表 const newList = getPlayList(this.playList[nextIndex].courseId, this.playList[nextIndex].audioId) // 當前音頻在這5條數據中的索引 const index = newList.findIndex(item => item.audioId === this.playList[nextIndex].audioId) // 更新到vuex this.updateCurrentIndex(index) this.updatePlayList(newList) } } } 

這裏的getPlayList方法是上面講過的,原本是從api中直接獲取的,爲了不每次都從api直接獲取,因此須要改一下,先讀storage,若無則從api獲取:

import { loadPlayList } from '@/api/audio' async function getPlayList (courseId, currentAudioId) { // 先從緩存列表中拿 const playList = wx.getStorageSync('playList') if (playList && playList.length > 0 && courseId === playList[0].courseId) { // 命中緩存,則從直接返回 return subPlayList(playList, currentAudioId) } else { // 沒有命中緩存,則從api中獲取 const list = await loadPlayList(courseId) wx.setStorage({ key: 'playList', data: list }) return subPlayList(list, currentAudioId) } } 

播放上一首也是同理,就不贅述了。

PS: 將vuex中的數據精簡後,我所作的小程序在播放音頻時刷其餘頁面已經很是流暢啦,效果很是好。

6、動畫優化

這個問題在mpvue開發音頻類小程序踩坑和建議已經講過了,感興趣的能夠移步看一眼,這裏只寫下概述:

  • 若是要使用動畫,儘可能用css動畫代替wx.createAnimation
  • 使用css動畫時建議開啓硬件加速

最後

大體總結一下上面所講的幾個要點:

  • 開發時打開Vue.config._mpTrace = true
  • 謹慎引入第三方庫,權衡收益。
  • 添加數據到data中時要剋制,能精簡儘可能精簡。
  • 圖片記得要壓縮,圖片在顯示時才渲染。
  • vuex保持數據精簡,必要時可先存storage。

性能優化是一個永不止步的話題,我也還在摸索,不足之處還請你們指點和分享。

 

 

 
 
 
相關文章
相關標籤/搜索