云裂变营销网
标题:
【小程序】爆肝 3 天总结的微信小程序优化指南(收藏夹吃灰 ...
[打印本页]
作者:
匿名
时间:
2022-9-19 12:13
标题:
【小程序】爆肝 3 天总结的微信小程序优化指南(收藏夹吃灰 ...
前言
大家好,我是
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=&#34;{{batchSetRecycleData}}&#34; id=&#34;recycleId&#34; catchscroll=&#34;onScroll&#34;>
<view slot=&#34;before&#34;>长列表前面的内容</view>
<recycle-item wx:for=&#34;{{recycleList}}&#34; wx:key=&#34;id&#34;>
<view>
<image style=&#39;width:80px;height:80px;float:left;&#39; src=&#34;{{item.image_url}}&#34;></image>
{{item.idx+1}}. {{item.title}}
</view>
</recycle-item>
<view slot=&#34;after&#34;>长列表后面的内容</view>
</recycle-view>
复制代码
list.js
const createRecycleContext = require(&#39;miniprogram-recycle-view&#39;)
Page({
onReady: function () {
var ctx = createRecycleContext({
id: &#39;recycleId&#39;,
dataKey: &#39;recycleList&#39;,
page: this,
itemSize: this.itemSizeFunc
})
ctx.append([{
image_url: &#39;https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/09883c4389f642829c96b6216eebea85~tplv-k3u1fbpfcp-watermark.image?&#39;,
idx: 1,
title: &#39;你好&#39;
}])
// ctx.update(beginIndex, list)
// ctx.destroy()
},
onScroll() {
console.log(&#39;scroll&#39;)
},
itemSizeFunc: function (item, idx) {
console.log(item)
return {
width: 375,
height: 100
}
}
})
复制代码
总结
代码写起来不是那么舒服,组件使用不友好,但是对性能确实还不错。
使用页面容器
有时候我们在页面上会弹出一些特定的半屏窗口,例如登录窗口。这时候如果用户在iOS设备上使用了左滑手势或者是在Android设备上单击了物理返回键,会造成页面跳转到上一个页面,这在大多数情况下它并不是用户的真实本意,用户可能只是想将当前弹出的半屏的假页面给它关掉。
page-container页面容器,是不需要引入便可以使用。
使用
<page-container :show=&#34;{{show}}&#34;>
...
</page-container>
复制代码
总结
这个组件其实就是防止用户的左滑或者按键返回误操作,导致整个页面回退的问题。
优化动画效果
实现动画的方式:
是使用Animation对象,实现CSS动画
使用页面或组件对象,拥有的animate方法实现的关键帧动画
使用滚动事件驱动的响应式动画
通过WXS脚本,实现了一个样式动画
通过第三方库实现animate.css
代码按需注入
代码懒加载
使用
// app.json
&#34;lazyCodeLoading&#34;: &#34;requiredComponents&#34;
复制代码
出现以下提示,代表成功
静态初始化渲染缓存
第一次页面运行时,由微信客户端负责将页面在本地的某个区域缓存起来,下次在真正的页面未加载完成前,先展示这个缓存过的页面。
使用
// xxxx.json
&#34;initialRenderingCache&#34;: &#34;static&#34;
复制代码
动态初始化渲染缓存
与静态初始化渲染缓存,动态初始化渲染缓存可以设置动态缓存的数据,放在onReady生命周期中
使用
// xxx.json
&#34;initialRenderingCache&#34;: &#34;dynamic&#34;
// xxx.js
// 设置动态缓存数据
this.setInitialRenderingCache({
customersList: customerList
})
复制代码
如果出现以下的信息,不用在意。
在手机里会有一条成功的信息:
update view with init data
复制代码
分包
小程序包大小规定,单个代码包不超过2MB,总包大小不超过20MB,所以到我们业务庞大,代码量大导致项目大小上升,就必须用分包的形式,解决这个问题。
使用
// app.json
{
&#34;pages&#34;: [
// &#34;pages/user/index&#34;
],
&#34;subpackages&#34;: [
{
&#34;root&#34;: &#34;user&#34;,
&#34;pages&#34;: [
&#34;pages/user/index&#34;
]
}
]
}
复制代码
独立分包
使用
{
&#34;pages&#34;: [
// &#34;pages/user/index&#34;
// &#34;pages/goods/index&#34;
],
&#34;subpackages&#34;: [
{
&#34;root&#34;: &#34;user&#34;,
&#34;pages&#34;: [
&#34;pages/user/index&#34;
]
},
{
&#34;root&#34;: &#34;goods&#34;,
&#34;pages&#34;: [
&#34;pages/goods/index&#34;
],
&#34;independent&#34;: true // 独立分包关键配置
}
]
}
// pages/goods/index.js
const app = getApp({allowDefault: true}) // 独立分包中getApp为空,这里设置默认值
复制代码
独立分包之后访问独立分包页面,主包和组件是不会被加载的,所以要分实际业务场景,进行分包
分包预加载
使用
&#34;preloadRule&#34;: {
// 分包root路径+后面的路径
&#34;goods/pages/detail/index&#34;: {
&#34;network&#34;: &#34;all&#34;,
&#34;packages&#34;: [
&#34;__APP__&#34;
]
}
}
复制代码
看到这个提示,表示成功
总结
tabbar最好自己实现
所有在根目录下的文件,如果没有分包,都会打包进主包,所以一般情况下,只放一个首页进主包
分包配置需要指定一个root目录,该目录下所有的文件都会自动被分割到这个分包里
对于相对独立的页面(例如分享页),可以进行独立分包,独立分包的页面会有「返回首页的按钮」,一般队里分包里,都需要设置分包预加载
使用占位组件
使用占位组件延迟加载自定义组件
使用 创建一个名称为index_addons的一个组件分包
// app.json
&#34;subpackages&#34;: [
{
&#34;root&#34;: &#34;index_addons&#34;,
&#34;pages&#34;: []
}
]
复制代码
修改主页配置
{
&#34;componentPlaceholder&#34;: {
&#34;stopwatch&#34;: &#34;view&#34; // 欲用作占位组件
}
}
复制代码
在stopwatch中添加代码
调试
设置为2g网
本地设置里,设置懒注入占位组件调试
如果没有这个选项,请确认是否有配置 &#34;lazyCodeLoading&#34;: [requiredComponents]
分包异步化
require
require.async 注意,只支持相对路径
使用
const { default: getNavList } = await require.async(&#34;../../../index_addons/compopnents/get_nav_list.js&#34;)
复制代码
视图代码优化技巧
动态列表渲染里优化wx:key的使用
确保wx:key的值是唯一值
如果列表元素是单一的基本数据类型,并且是唯一的,这时候我们可以直接写成
this,这里这个
this,就代表当前数据列表里面的数据元素
<view wx:for=&#34;{{textList}}&#34; wx:key=&#34;*this&#34;></view>
复制代码
如果列表元素是静态的,只渲染一次,那么可以直接用index
<view wx:for=&#34;{{swipers}}&#34; wx:key=&#34;index&#34;></view>
复制代码
如果数据是从后台获取的,那么wx:key需要有一个唯一值
<view wx:for=&#34;{{customerList}}&#34; wx:key=&#34;{{index}}&#34;></view>
复制代码
绑定视图事件
使用catch代替bind,减少dataset的数据运输量,因为我们在大多数情况下,我们的事件不需要冒泡,所以bind很浪费性能,catch是不会冒泡的,事件传递某些信息的时候,需要什么传什么,不要一刀切,图省事,传递一个很大的对象,如果需要整个对象,建议使用index进行传递
<view class=&#34;submit-btn&#34; catchbind=&#34;onViewDetail&#34; data-index=&#34;index&#34;>view detail</view>
复制代码
防抖&节流
节流
节流顾名思义就是控制某段JS代码的执行频率
function throttle(method, wait = 50) {
let previous = 0
return function(...agrs) {
let context = this
let now = new Date().getTime()
if(now - previouts > wait) {
method.apply(context, args)
previous = now
} else {
console.log(&#34;节流少许&#34;)
}
}
}
复制代码
防抖
顾名思义就是防止抖动,避免把一次时间当做多次处理,敲击键盘就是一个经常都会遇到的防抖操作场景
function debounce(func, wait = 50) {
let timer = null
return function (...args) {
const contest = this
if(timer) {
clearTimeout(timer)
console.log(&#34;防抖少许&#34;)
}
timer = setTimeout(() => {
func.call(context, ...args)
}, wait)
}
}
复制代码
组件中使用
const {default:debounce} = require(../../../library/optimus/debounce.js)
const {default:throttle} = require(../../../library/optimus/throttle.js)
onScrolllToLower: throttle(function(e) {
...
})
onTapCustomerItem: debounce(function(e) {
..
})
复制代码
尽量少的使用重渲染和wxml标签
官方建议:
总页面节点数少于1000个,节点树深度层级少于30层,子节点数不大于60个
所以,在开发过程中,能用<text>,就不用<view>,能用文本,就不用<text>,对于循环列表中,能用<block>就不用<view>
WXSS优化技巧
给滚动组件开启惯性滚动
-webkit-overflow-scrolling: touch;;
复制代码
使用hover-class实现按钮的单击态
<button class=&#34;submit-btn&#34; hover-class=&#34;submit-btn__hover&#34;></button>
.submit-btn {
...
}
.submit-btn__hover {
...
}
复制代码
使用gulp工具删除无用的wxss样式代码
安装
npm install gulp -g
npm install --save-dev gulp gulp-cleanwxss
复制代码
配置 /tools/gulpfile.js
const gulp = require(&#34;gulp&#34;)
const cleanwxss = require(&#34;gulp-cleanwxss&#34;)
// 处理父目录下的样式文件,输出到当前目录下的dist
gulp.task(&#34;default&#34;, (done) => {
group.src(&#34;../minniprogram/index/pages/*/*.wxss&#34;)
.pipe(cleanwxss({ log: true }))
.pipe(gulp.dest(&#34;./dist&#34;))
done()
})
// 处理分包下的样式文件
gulp.task(&#34;goods&#34;, (done) => {
group.src(&#34;../minniprogram/goods/pages/*/*.wxss&#34;)
.pipe(cleanwxss({ log: true }))
.pipe(gulp.dest(&#34;./dist&#34;))
done()
})
复制代码
执行
gulp default
gulp goods
复制代码
CV
将生成出来的样式文件复制到对应目录即可
UI交互优化技巧
使用padding扩大可点击区域大小
.submit-btn {
...
padding: 10px;
}
复制代码
使用伪元素扩大可点击区域大小
.submit-btn {
position: relative;
...
}
.submit-btn::after {
content: &#39;&#39;,
position: absolute;
top: -20px;
right: -20px;
bottom: -20px;
left: -20px;
}
复制代码
脚本优化技巧
清理定时器
clearTimeout(timer1)
复制代码
使用wx.onXxx全局绑定,有一个监听,必须有一个反监听
// 及时释放本页范围内添加的全局监听器
onLoad() {
wx.onThemeChange(this.themeChangeHandler)
}
onUnload() {
wx.offThemeChange(this.themeChangeHandler)
}
复制代码
模拟器测试需要在app.json中添加
&#34;darkMode&#34;: &#34;true&#34;
复制代码
小心使用全局对象,适当时机清理
setData调用优化
不要多次分开调用setData,尽量合并
// bad
if(this.data.gender === &#39;1&#39;) {
this.setData({
genderName: &#39;男&#39;
})
} else if(this.data.gender === &#39;0&#39;) {
this.setData({
genderName: &#39;女&#39;
})
} else {
this.setData({
genderName: &#39;未知&#39;
})
}
// good
let genderName = &#39;未知&#39;
if(this.data.gender === &#39;1&#39;) genderName = &#39;男&#39;
if(this.data.gender === &#39;0&#39;) genderName = &#39;女&#39;
this.setData({
genderName,
})
复制代码
不准备渲染的数据,不要放在data对象中
Page({
allList: [],
pageNum: 1,
pageSize: 10,
data: {
list: []
}
})
复制代码
通过index局部更新列表数据
this.setData({
[this.data.customerList[index].title]: item.title+=&#39;---&#39;
})
复制代码
网络请求优化技巧
减少不必要的网络请求,使用本地缓存数据
技术点:Storage
优化网络请求参数,提高网络请求效率
enableCache: true,
enableHttp2: true,
enableQuic: true
复制代码
优化网络请求的并发数,分优先级
npm i priority-async-queue
复制代码
request.js
const PriorityAsyncQueue = require(&#39;priority-async-queue&#39;)
const queue = new PriorityAsyncQueue(10) // default 10
const low = &#34;low&#34;, normal = &#34;normal&#34;, mid = &#34;mid&#34;, high = &#34;high&#34;, &#34;urgent&#34; = &#34;urgent&#34;
export const priority = { low, normal, mid, high, urgent }
....
return new Promise((resolve, reject) => {
queue.addTask({ priority }. () => {
wx.request(Object.assgin(args,{
success: resolve,
fail: reject
})
})
})
复制代码
图片优化技巧
减少图片的请求次数
压缩图片大小
尽可能使用CDN图片或图片链接
尽可能使用webp格式图片
完结
小程序的优化大致就是从这些反面入手了,觉得有用的同学,点个赞,收个藏吧!
欢迎光临 云裂变营销网 (https://www.yunliebian.com/yingxiao/)
Powered by Discuz! X3.4