mpvue性能优化实战技巧(小结)


Posted in Javascript onApril 17, 2019

最近一直在折腾mpvue写的微信小程序的性能优化,分享下实战的过程。

先上个优化前后的图:

mpvue性能优化实战技巧(小结)

可以看到打包后的代码量从813KB减少到387KB,Audits体验评分从BA,效果还是比较明显的。其实这个指标说明不了什么,而且轻易就可以做到,更重要的是优化小程序运行过程中的卡顿感,请耐心往下看。

常规优化

常规的Web端优化方法在小程序中也是适用的,而且不可忽视。

一、压缩图片

这一步最简单,但是容易被忽视。在tiny上在线压缩,然后下载替换即可。

mpvue性能优化实战技巧(小结)

我这项目的压缩率高达72%,可以说打包后的代码从813KB降到387KB大部分都是归功于压缩图片了。

二、移除无用的库

我之前在项目中使用了Vant Weapp,在static目录下引入了整个库,但实际上我只使用了button,field,dialog等几个组件,实在是没必要。

所以干脆移除掉了,微信小程序自身提供的buttonwx.showModal等一些组件基本可以满足需求,自己手写一下样式也不用花什么时间。

在这里建议大家,在微信小程序中,尽量避免使用过多的依赖库

不要贪图方便而引入一些比较大的库,小程序不同于Web,限制比较多,能自己写一下就尽量自己写一下吧。

小程序的优化

咱们首先得看一下官方优化建议,大多是围绕这个建议去做。

一、开启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更新的数据量。如图:

mpvue性能优化实战技巧(小结)

如果数据更新量很大,会明显感觉小程序运行卡顿,反之就流畅。因此我们可以根据这个指标,逐步找出性能瓶颈并解决掉。

二、精简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>

代码也更简洁清爽。

看一下做了上面操作的前后对比图,使用体验上也流畅了很多。

mpvue性能优化实战技巧(小结)

三、swiper优化

小程序自身提供的swiper组件性能上不是很好,使用时要注意。参考着两个思路:

一般情况下,推荐使用后者。我在项目中尝试使用了前者,同样能达到很好的效果,请继续看下面的分享。

五、善用storage

1.为什么说要善用storage

由于小程序的内存非常宝贵,占用内存过大会非常卡顿,因此最好尽可能少的将数据放到内存中,即vuex存的数据要尽可能少。而小程序的storage支持单个 key允许存储的最大数据长度为 1MB,所有数据存储上限为 10MB

所以可以将一些相对取用不频繁的数据放进storage中,需要时再将这些数据放进内存,从而缓解内存的紧张,有点类似Windows中虚拟内存的概念。

2.storage换内存的实例

这个例子讲的会有点??拢?嬲?苡玫降呐笥芽梢韵晗缚聪隆?/blockquote>

上面讲到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个上一首,没有下一首

有点??拢?行巳さ耐?ё邢缸聊ハ拢?蘼鄣鼻耙羝翟谀模?际贾毡Vち四玫降鼻耙羝登昂蟮淖疃?条数据。

接下来就是维护播放上一首或下一首时保证当前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中的数据精简后,我所做的小程序在播放音频时刷其他页面已经非常流畅啦,效果非常好。

六、动画优化

这个问题在mpvue开发音频类小程序踩坑和建议已经讲过了,感兴趣的可以移步看一眼,这里只写下概述:

如果要使用动画,尽量用css动画代替wx.createAnimation使用css动画时建议开启硬件加速最后

大致总结一下上面所讲的几个要点:

  • 开发时打开Vue.config._mpTrace = true。
  • 谨慎引入第三方库,权衡收益。
  • 添加数据到data中时要克制,能精简尽量精简。
  • 图片记得要压缩,图片在显示时才渲染。
  • vuex保持数据精简,必要时可先存storage。

性能优化是一个永不止步的话题,我也还在摸索,不足之处还请大家指点和分享。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS俄罗斯方块,包含完整的设计理念
Dec 11 Javascript
分享精心挑选的23款美轮美奂的jQuery 图片特效插件
Aug 14 Javascript
JavaScript中Math对象方法使用概述
Jan 02 Javascript
简单的JavaScript互斥锁分享
Feb 02 Javascript
减少访问DOM的次数提升javascript性能
Feb 24 Javascript
使用AngularJS和PHP的Laravel实现单页评论的方法
Jun 19 Javascript
Bootstrap被封装的弹层
Jul 20 Javascript
angularJS 如何读写缓冲的方法(推荐)
Aug 06 Javascript
深入理解jQuery()方法的构建原理
Dec 05 Javascript
vue中如何实现pdf文件预览的方法
Jul 12 Javascript
基于Cesium绘制抛物弧线
Nov 18 Javascript
使用vue-element-admin框架从后端动态获取菜单功能的实现
Apr 29 Vue.js
node.js监听文件变化的实现方法
Apr 17 #Javascript
vue中格式化时间过滤器代码实例
Apr 17 #Javascript
postman自定义函数实现 时间函数的思路详解
Apr 17 #Javascript
vue指令之表单控件绑定v-model v-model与v-bind结合使用
Apr 17 #Javascript
记一次Vue.js混入mixin的使用(分权限管理页面)
Apr 17 #Javascript
详解js获取video任意时间的画面截图
Apr 17 #Javascript
解决vue跨域axios异步通信问题
Apr 17 #Javascript
You might like
PHP执行linux系统命令的常用函数使用说明
2010/04/27 PHP
按上下级层次关系输出内容的PHP代码
2010/07/17 PHP
php设计模式 Factory(工厂模式)
2011/06/26 PHP
PHP 杂谈《重构-改善既有代码的设计》之三 重新组织数据
2012/04/09 PHP
利用php实现禁用IE和火狐的缓存问题
2012/12/03 PHP
PHP开发框架kohana中处理ajax请求的例子
2014/07/14 PHP
php处理复杂xml数据示例
2016/07/11 PHP
Yii CFileCache 获取不到值的原因分析
2017/02/08 PHP
javascript实现的距离现在多长时间后的一个格式化的日期
2009/10/29 Javascript
Javascript 键盘事件的组合使用实现代码
2012/05/04 Javascript
JS自定义功能函数实现动态添加网址参数修改网址参数值
2013/08/02 Javascript
laytpl 精致巧妙的JavaScript模板引擎
2014/08/29 Javascript
浅谈javascript中this在事件中的应用
2015/02/15 Javascript
代码分析jQuery四种静态方法使用
2015/07/23 Javascript
深入浅出ES6之let和const命令
2016/08/25 Javascript
Vue.JS入门教程之处理表单
2016/12/01 Javascript
JS实现面向对象继承的5种方式分析
2018/07/21 Javascript
微信小程序用户授权,以及判断登录是否过期的方法
2019/05/10 Javascript
[01:07:19]DOTA2-DPC中国联赛 正赛 CDEC vs XG BO3 第一场 1月19日
2021/03/11 DOTA
Python中的rjust()方法使用详解
2015/05/19 Python
在Python中使用defaultdict初始化字典以及应用方法
2018/10/31 Python
Python散点图与折线图绘制过程解析
2019/11/30 Python
django ListView的使用 ListView中获取url中的参数值方式
2020/03/27 Python
Stylenanda中文站:韩国一线网络服装品牌
2016/12/22 全球购物
性能服装:HYLETE
2018/08/14 全球购物
大学毕业生的自我鉴定
2013/11/30 职场文书
自我评价范文分享
2014/01/04 职场文书
中国梦的演讲稿
2014/01/08 职场文书
经济担保书范文
2014/04/02 职场文书
2014五一国际劳动节活动总结范文
2014/04/14 职场文书
我为党旗添光彩演讲稿
2014/09/13 职场文书
党员批评与自我批评思想汇报(集锦)
2014/09/14 职场文书
代领学位证书毕业证书委托书
2014/09/30 职场文书
统计工作个人总结
2015/03/03 职场文书
Nginx安装完成没有生成sbin目录的解决方法
2021/03/31 Servers
css filter和getUserMedia的联合使用
2022/02/24 HTML / CSS