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中的数学函数集合
May 08 Javascript
百度地图api如何使用
Aug 03 Javascript
JS 终止执行的实现方法
Nov 24 Javascript
Bootstrap Table使用心得总结
Nov 29 Javascript
JavaScript自定义浏览器滚动条兼容IE、 火狐和chrome
Jan 05 Javascript
js实现文字选中分享功能
Jan 25 Javascript
vue-cli实现多页面多路由的示例代码
Jan 30 Javascript
Vue的H5页面唤起支付宝支付功能
Apr 18 Javascript
手把手教你使用TypeScript开发Node.js应用
May 06 Javascript
VueCli3.0中集成MockApi的方法示例
Jul 05 Javascript
javascript自定义日期比较函数用法示例
Jul 22 Javascript
微信小程序全选多选效果实现代码解析
Jan 21 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
浅析ThinkPHP中的pathinfo模式和URL重写
2014/01/06 PHP
PHP中的reflection反射机制测试例子
2014/08/05 PHP
php动态添加url查询参数的方法
2015/04/14 PHP
详解PHP实现异步调用的4种方法
2016/03/14 PHP
PHP+ajax实现获取新闻数据简单示例
2018/05/08 PHP
jquery实现图片滚动效果的简单实例
2013/11/23 Javascript
再探JavaScript作用域
2014/09/24 Javascript
jQuery实现ajax调用WCF服务的方法(附带demo下载)
2015/12/04 Javascript
JavaScript实现公历转农历功能示例
2017/02/13 Javascript
微信小程序form表单组件示例代码
2018/07/15 Javascript
浅析JS中回调函数及用法
2018/07/25 Javascript
基于js Canvas实现二次贝塞尔曲线
2018/12/25 Javascript
详解微信UnionID作用
2019/05/15 Javascript
详解如何搭建mpvue框架搭配vant组件库的小程序项目
2019/05/16 Javascript
vue中的v-if和v-show的区别详解
2019/09/01 Javascript
简单了解JavaScript sort方法
2019/11/25 Javascript
Vue ElementUI实现:限制输入框只能输入正整数的问题
2020/07/31 Javascript
vue 子组件修改data或调用操作
2020/08/07 Javascript
[43:35]EG vs Winstrike 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
在Django中创建动态视图的教程
2015/07/15 Python
python进行两个表格对比的方法
2018/06/27 Python
Python用Try语句捕获异常的实例方法
2019/06/26 Python
python射线法判断一个点在图形区域内外
2019/06/28 Python
Python 迭代,for...in遍历,迭代原理与应用示例
2019/10/12 Python
Python基于DB-API操作MySQL数据库过程解析
2020/04/23 Python
Python3爬虫发送请求的知识点实例
2020/07/30 Python
基于python获取本地时间并转换时间戳和日期格式
2020/10/27 Python
css3 图片圆形显示 如何CSS将正方形图片显示为圆形图片布局
2014/10/10 HTML / CSS
台湾乐天市场:日本No.1的网路购物网站
2017/03/22 全球购物
DHC美国官网:日本通信销售第一的化妆品品牌
2017/11/12 全球购物
KOHLER科勒美国官网:国际著名卫浴橱柜领先品牌
2020/06/27 全球购物
最新结婚典礼主持词
2014/03/14 职场文书
感恩教育活动总结
2014/05/05 职场文书
网络文明传播志愿者活动方案
2014/08/20 职场文书
2014年销售人员工作总结
2014/11/27 职场文书
企业爱心捐款倡议书
2015/04/27 职场文书