前言
大家好,我是HoMeTown,最近要做一个小程序的项目,项目启动之前,回顾自己之前做过的小程序,感觉做的还是不够好,最近学习了一下小程序优化方案,这块总结一份个人笔记,以便参考,同时分享给大家,共勉。
关于微信小程序
微信小程序上线至今已经大约已经有5年
的时间,5年的时间,小程序的开发能力经过微信团队的不断努力优化,已经日益完善,不再像之前一样,翻翻文档就能完成一个简易的项目了,特别是性能优化方面,会优化与不会优化更是对产品的效果有着决定性的影响。
哪些部分需要优化?
- 首页白屏现象
- Loading加载时间过长,页面好久不显示
- 点击页面链接,页面跳转迟钝
- 按钮单击无响应
- 长内容列表,内容越多,越卡顿
- ......
有了优化的方向,就可以根据问题逐一诊断了
小程序的启动流程?
运行载体
- iOS、Mac
- Android、PC
- 微信开发者工具
环境准备
- 小程序运行进程及运行环境准备
- 代码包下载、校验及初始化
- 视图层系统组件、WebView容器和原生组件的初始化
- 原生组件的初始化
- 逻辑层JS引擎初始化及域创建
代码注入
- 框架及第三方基础代码的初始化
- 小程序基础库注入
- 扩展库注入
- 插件、自定义组件注入
- 开发者代码注入
- 逻辑层代码「这里会派发
App.onLaunch
还有App.onShow
这些事件」 - 视图层代码
首屏渲染
- 逻辑层页面初始化,这个时间点是
initDataSendTime
,会派发Page.onLoad
事件 - 视图层时间点走到
viewLayerReaderStartTime
,会派发Page.onShow
事件 - 开发者代码从后端拉取,准备data数据
- 页面渲染
- 视图层时间点走到
viewLayerReaderEndTime
,会派发Page.onReady
事件,意味着首屏渲染完成
启动方式
冷启动
小程序在用户设备上第一次打开或者是销毁之后再打开,或者是30分钟以后
热启动
热启动是相对于冷启动而言的,热启动是小程序启动的一种优化机制,小程序进入后台30分钟以内再次进到前台,可以直接从后台状态然后回复到前台,所以,在这种情况下,刚刚那个代码注入
、首屏渲染
等基础工作就不会再执行了,设置App.onLaunch
、Page.onLoad
等这些一次性的生命周期,也不会有了。
结论由此,可以得出,要做小程序的性能优化,主要是做冷启动
这块的优化以及运行时渲染性能的一个优化,小程序冷启动流程里涉及到一些程序和生命周期。
生命周期
在小程序中App和Page都有他们各自的生命周期函数。
App
- onLaunch,监听小程序初始化的事件
- onShow 监听小程序启动或切前台的事件
- onHide 监听小程序切后台的时间
Page
- onLoad 监听页面加载
- onShow 监听页面显示
- onReady 监听页面初次渲染完成
- onHide 监听页面隐藏
- onUnload 监听页面卸载
优化技巧
使用骨架屏
骨架屏的目的是为了减缓用户等待的情绪
使用
如图,在小程序开发者工具中,点击这里,生成骨架屏代码
然后他会生成两个文件:index.skeleton.wxml
& index.skeleton.wxss
分别在index.wxml
& index.wxss
引入
总结
- 在data数据对象中默认设置loading等于true。
- 不要直接修改生成的骨架屏的一个代码。通过配置去修改。
- **不要过度去使用骨架屏。一般只给主页去添加骨架屏效果 **
优化长列表
优化长列表,使用recycle-view
& recycle-item
虚拟DOM组件,在渲染的时候需要知道每个循环单元的一个高度,这是消费代码需要传递给组件的,在recycle-view组件里面,用户滑动的是数据而不是组件本身。
使用
首先初始化package.json
// npm初始化package.json文件 npm init -y 复制代码
安装
// 安装 miniprogram-recycle-view插件 npm install --save miniprogram-recycle-view 复制代码
构建依赖
miniprogram_npm
目录
组件内引用
代码中使用
list.wxml
<recycle-view batch="{{batchSetRecycleData}}" id="recycleId" catchscroll="onScroll"> <view slot="before">长列表前面的内容</view> <recycle-item wx:for="{{recycleList}}" wx:key="id"> <view> <image style='width:80px;height:80px;float:left;' src="{{item.image_url}}"></image> {{item.idx+1}}. {{item.title}} </view> </recycle-item> <view slot="after">长列表后面的内容</view> </recycle-view> 复制代码
list.js
const createRecycleContext = require('miniprogram-recycle-view') Page({ onReady: function () { var ctx = createRecycleContext({ id: 'recycleId', dataKey: 'recycleList', page: this, itemSize: this.itemSizeFunc }) ctx.append([{ image_url: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/09883c4389f642829c96b6216eebea85~tplv-k3u1fbpfcp-watermark.image?', idx: 1, title: '你好' }]) // ctx.update(beginIndex, list) // ctx.destroy() }, onScroll() { console.log('scroll') }, itemSizeFunc: function (item, idx) { console.log(item) return { width: 375, height: 100 } } }) 复制代码
总结
代码写起来不是那么舒服,组件使用不友好,但是对性能确实还不错。
使用页面容器
有时候我们在页面上会弹出一些特定的半屏窗口,例如登录窗口。这时候如果用户在iOS设备上使用了左滑手势或者是在Android设备上单击了物理返回键,会造成页面跳转到上一个页面,这在大多数情况下它并不是用户的真实本意,用户可能只是想将当前弹出的半屏的假页面给它关掉。
page-container
页面容器,是不需要引入便可以使用。
使用
<page-container :show="{{show}}"> ... </page-container> 复制代码
总结
这个组件其实就是防止用户的左滑
或者按键返回
误操作,导致整个页面回退的问题。
优化动画效果
实现动画的方式:
- 是使用Animation对象,实现CSS动画
- 使用页面或组件对象,拥有的animate方法实现的关键帧动画
- 使用滚动事件驱动的响应式动画
- 通过WXS脚本,实现了一个样式动画
- 通过第三方库实现
animate.css
代码按需注入
代码懒加载
使用
// app.json "lazyCodeLoading": "requiredComponents" 复制代码
出现以下提示,代表成功
静态初始化渲染缓存
第一次页面运行时,由微信客户端负责将页面在本地的某个区域缓存起来,下次在真正的页面未加载完成前,先展示这个缓存过的页面。
使用
// xxxx.json "initialRenderingCache": "static" 复制代码
动态初始化渲染缓存
与静态初始化渲染缓存,动态初始化渲染缓存可以设置动态缓存的数据,放在onReady
生命周期中
使用
// xxx.json "initialRenderingCache": "dynamic" 复制代码
// xxx.js // 设置动态缓存数据 this.setInitialRenderingCache({ customersList: customerList }) 复制代码
如果出现以下的信息,不用在意。
在手机里会有一条成功的信息:
update view with init data 复制代码
分包
小程序包大小规定,单个代码包不超过2MB,总包大小不超过20MB,所以到我们业务庞大,代码量大导致项目大小上升,就必须用分包的形式,解决这个问题。
使用
// app.json { "pages": [ // "pages/user/index" ], "subpackages": [ { "root": "user", "pages": [ "pages/user/index" ] } ] } 复制代码
独立分包
使用
{ "pages": [ // "pages/user/index" // "pages/goods/index" ], "subpackages": [ { "root": "user", "pages": [ "pages/user/index" ] }, { "root": "goods", "pages": [ "pages/goods/index" ], "independent": true // 独立分包关键配置 } ] } 复制代码
// pages/goods/index.js const app = getApp({allowDefault: true}) // 独立分包中getApp为空,这里设置默认值 复制代码
独立分包之后访问独立分包页面,主包和组件是不会被加载的,所以要分实际业务场景,进行分包
分包预加载
使用
"preloadRule": { // 分包root路径+后面的路径 "goods/pages/detail/index": { "network": "all", "packages": [ "__APP__" ] } } 复制代码
看到这个提示,表示成功
总结
- tabbar最好自己实现
- 所有在根目录下的文件,如果没有分包,都会打包进主包,所以一般情况下,只放一个首页进主包
- 分包配置需要指定一个
root
目录,该目录下所有的文件都会自动被分割到这个分包里 - 对于相对独立的页面(例如分享页),可以进行独立分包,独立分包的页面会有「返回首页的按钮」,一般队里分包里,都需要设置
分包预加载