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 相关文章推荐
服务器端的JavaScript脚本 Node.js 使用入门
Mar 07 Javascript
用Jquery重写windows.alert方法实现思路
Apr 03 Javascript
jquery text(),val(),html()方法区别总结
Nov 04 Javascript
ExtJS实现文件下载的方法实例
Nov 09 Javascript
jquery日历控件实现方法分享
Mar 07 Javascript
最简单的JavaScript图片轮播代码(两种方法)
Dec 18 Javascript
AngularJs定制样式插入到ueditor中的问题小结
Aug 01 Javascript
jQuery中Nicescroll滚动条插件的用法
Nov 10 Javascript
javascript基础知识讲解
Jan 11 Javascript
axios基本入门用法教程
Mar 25 Javascript
jQuery实现的电子时钟效果完整示例
Apr 28 jQuery
vue实现添加与删除图书功能
Oct 07 Javascript
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
【星际争霸1】人族1v7家ZBath
2020/03/04 星际争霸
关于WordPress的SEO优化相关的一些PHP页面脚本技巧
2015/12/10 PHP
枚举JavaScript对象的函数
2006/12/22 Javascript
Prototype使用指南之ajax
2007/01/10 Javascript
一个简单的JavaScript 日期计算算法
2009/09/11 Javascript
使用javascript:将其它类型值转换成布尔类型值的解决方法详解
2013/05/07 Javascript
js使用split函数按照多个字符对字符串进行分割的方法
2015/03/20 Javascript
javascript实时获取鼠标坐标值并显示的方法
2015/04/30 Javascript
JavaScript实现广告的关闭与显示效果实例
2015/07/02 Javascript
JavaScript数据存储 Cookie篇
2016/07/02 Javascript
js获取json中key所对应的value值的简单方法
2020/06/17 Javascript
BootStrap表单控件之复选框checkbox和单选择按钮radio
2017/05/23 Javascript
(模仿京东用户注册)用JQuery实现简单表单验证,初学者必看
2018/01/08 jQuery
Angular使用动态加载组件方法实现Dialog的示例
2018/05/11 Javascript
vue+axios 前端实现的常用拦截的代码示例
2018/08/23 Javascript
深入浅出 Vue 系列 -- 数据劫持实现原理
2019/04/23 Javascript
vue-以文件流-blob-的形式-下载-导出文件操作
2020/08/07 Javascript
[03:53]2016国际邀请赛中国区预选赛第三日TOP10精彩集锦
2016/06/29 DOTA
python简单读取大文件的方法
2016/07/01 Python
python实现神经网络感知器算法
2017/12/20 Python
python语音识别实践之百度语音API
2018/08/30 Python
Python中numpy模块常见用法demo实例小结
2019/03/16 Python
Python绘制堆叠柱状图的实例
2019/07/09 Python
django 将自带的数据库sqlite3改成mysql实例
2020/07/09 Python
Python调用高德API实现批量地址转经纬度并写入表格的功能
2021/01/12 Python
CSS3 3D旋转rotate效果实例介绍
2016/05/03 HTML / CSS
详解H5本地储存Web Storage
2017/07/03 HTML / CSS
基于HTML5 Canvas 实现弹出框效果
2017/06/05 HTML / CSS
美国领先的低折扣旅行网站:Hotwire
2019/01/19 全球购物
vue 中 get / delete 传递数组参数方法
2021/03/23 Vue.js
应届生保险求职信
2013/11/11 职场文书
师范大学应届生求职信
2013/11/21 职场文书
公司周年庆典邀请函
2014/01/12 职场文书
项目负责人任命书
2014/06/04 职场文书
任命书格式
2014/06/05 职场文书
大学生个人简历自荐信
2015/03/06 职场文书