【小程序】爆肝 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.onLaunchPage.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__"
        ]
    }
}

看到这个提示,表示成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZW8RQRvp-1667270755277)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/849fa956036d4e4c815be5af51cc1f60~tplv-k3u1fbpfcp-watermark.image?)]

总结

  • tabbar最好自己实现
  • 所有在根目录下的文件,如果没有分包,都会打包进主包,所以一般情况下,只放一个首页进主包
  • 分包配置需要指定一个root目录,该目录下所有的文件都会自动被分割到这个分包里
  • 对于相对独立的页面(例如分享页),可以进行独立分包,独立分包的页面会有「返回首页的按钮」,一般队里分包里,都需要设置分包预加载

使用占位组件

使用占位组件延迟加载自定义组件

使用
创建一个名称为index_addons的一个组件分包

// app.json
"subpackages": [
    {
        "root": "index_addons",
        "pages": []
    }
]

修改主页配置

{
    "componentPlaceholder": {
        "stopwatch": "view" // 欲用作占位组件
    }
}

在stopwatch中添加代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WM3vwA4f-1667270755277)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/495e86c0eae0479989a362810da61860~tplv-k3u1fbpfcp-watermark.image?)]

调试

  • 设置为2g网
  • 本地设置里,设置懒注入占位组件调试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rfbrh0m8-1667270755278)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5b8807674da347d4b71909ec45098392~tplv-k3u1fbpfcp-watermark.image?)]

如果没有这个选项,请确认是否有配置 "lazyCodeLoading": [requiredComponents]

分包异步化

  • require
  • require.async
    注意,只支持相对路径

使用

const { default: getNavList } = await require.async("../../../index_addons/compopnents/get_nav_list.js")

视图代码优化技巧

动态列表渲染里优化wx:key的使用

确保wx:key的值是唯一值

如果列表元素是单一的基本数据类型,并且是唯一的,这时候我们可以直接写成this,这里这个this,就代表当前数据列表里面的数据元素

<view wx:for="{{textList}}" wx:key="*this"></view>

如果列表元素是静态的,只渲染一次,那么可以直接用index

<view wx:for="{{swipers}}" wx:key="index"></view>

如果数据是从后台获取的,那么wx:key需要有一个唯一值

<view wx:for="{{customerList}}" wx:key="{{index}}"></view>
绑定视图事件

使用catch代替bind,减少dataset的数据运输量,因为我们在大多数情况下,我们的事件不需要冒泡,所以bind很浪费性能,catch是不会冒泡的,事件传递某些信息的时候,需要什么传什么,不要一刀切,图省事,传递一个很大的对象,如果需要整个对象,建议使用index进行传递

<view class="submit-btn" catchbind="onViewDetail" data-index="index">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("节流少许")
        }
    }
}
防抖

顾名思义就是防止抖动,避免把一次时间当做多次处理,敲击键盘就是一个经常都会遇到的防抖操作场景

function debounce(func, wait = 50) {
    let timer = null
    return function (...args) {
        const contest = this
        if(timer) {
            clearTimeout(timer)
            console.log("防抖少许")
        }
        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>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8xpMF0D7-1667270755278)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ef954f7253514eb0bc8dac0e34a8c7a2~tplv-k3u1fbpfcp-watermark.image?)]

WXSS优化技巧

给滚动组件开启惯性滚动
-webkit-overflow-scrolling: touch;;
使用hover-class实现按钮的单击态
<button class="submit-btn" hover-class="submit-btn__hover"></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("gulp")
const cleanwxss = require("gulp-cleanwxss")

// 处理父目录下的样式文件,输出到当前目录下的dist
gulp.task("default", (done) => {
    group.src("../minniprogram/index/pages/*/*.wxss")
        .pipe(cleanwxss({ log: true }))
        .pipe(gulp.dest("./dist"))
     done()
})

// 处理分包下的样式文件
gulp.task("goods", (done) => {
    group.src("../minniprogram/goods/pages/*/*.wxss")
        .pipe(cleanwxss({ log: true }))
        .pipe(gulp.dest("./dist"))
     done()
})

执行

gulp default
gulp goods

CV

将生成出来的样式文件复制到对应目录即可

UI交互优化技巧

使用padding扩大可点击区域大小
.submit-btn {
    ...
    padding: 10px;
}
使用伪元素扩大可点击区域大小
.submit-btn {
    position: relative;
    ...
}
.submit-btn::after {
    content: '',
    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中添加

"darkMode": "true"
小心使用全局对象,适当时机清理

setData调用优化

不要多次分开调用setData,尽量合并
// bad
if(this.data.gender === '1') {
    this.setData({
        genderName: '男'
    })
} else if(this.data.gender === '0') {
    this.setData({
        genderName: '女'
    })
} else {
    this.setData({
        genderName: '未知'
    })
}

// good
let genderName = '未知'
if(this.data.gender === '1') genderName = '男'
if(this.data.gender === '0') genderName = '女'
this.setData({
    genderName,
})
不准备渲染的数据,不要放在data对象中
Page({
    allList: [],
    pageNum: 1,
    pageSize: 10,
    data: {
        list: []
    }
})
通过index局部更新列表数据
this.setData({
    [this.data.customerList[index].title]: item.title+='---'
})

网络请求优化技巧

减少不必要的网络请求,使用本地缓存数据

技术点:Storage

优化网络请求参数,提高网络请求效率
enableCache: true,
enableHttp2: true,
enableQuic: true
优化网络请求的并发数,分优先级
npm i priority-async-queue

request.js

const PriorityAsyncQueue = require('priority-async-queue')
const queue = new PriorityAsyncQueue(10) // default 10

const low = "low", normal = "normal", mid = "mid", high = "high", "urgent" = "urgent"

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格式图片

完结

小程序的优化大致就是从这些方面入手了,觉得有用的同学,点个赞,收个藏吧!

往期回顾

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder HoMeTown

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值