- 视频无缝切换(ios黑一下问题也解决了),预加载图片资源等小程序二三点优化
缘由没啥别的理由,就是闲的。最近又开始写小程序了,不过心态不太一样了,因为其实大家都是js。你不能总是觉得小程序low,其实人家的思维逻辑都是互通的。 今天把一些性能优化的解决方案拿出来分享下。能比较有效的解决页面的一些卡顿和存在的一些问题 提示:博主用uniapp框架,但是原理都是一样的,一些uniapp的api,你把uni替换成wx就OK了 掘金原文地址:https://juejin.im/post/6881923388810461197/ 一、data数据的性能优化,基本操作这个其实和vue一个道理,所以大家不要把不参与渲染的数据定义在data里面了,不管是用了框架的还是原生小程序框架的。最好是大家把未定义渲染的数据放在created开始,进行约定规范,能大幅降低渲染成本。 再一个,如果你的数据不进行渲染操作,那么比如for循环赋值之类的操作,最好是先存储一下变量。最后再赋值回去。能减少无意间的性能开销,这个主要是针对vue,react框架这类框架。 举例: this.list.map() 改为 const list = this.list list.map() this.list = list 这样不只是性能优化,你的代码整洁度也能大幅上升 二、路由优化,能大幅降低性能问题首先微信小程序为了性能优化,对页面进行了10层最大缓存的处理,并且当10层缓存满了之后,navigateTo函数会直接报错,无法使用。除了我们一般经常需要注意页面层级问题外,其实还有别的方式来解决这个问题。当然一般的电商购物类的页面没有这么苛刻。但是这次公司的app是视频播放类app,本身视频播放就很吃性能,而且看似页面就那么几个,但是极度容易出现层级满了,然后页面跳转失效问题。 如果用reLaunch会释放资源,那么缓存失效,更卡 redirectTo呢依旧无法避免层级超过10层问题 所以就有了下面这个函数 /* { url: 'pages/index', type: 'navigate' //redirect分别对应navigateTo和redirectTo } */ const pageJump = ({ url, type = 'navigate' }) => { const pageStack = getCurrentPages() const pageLen = pageStack.length const home = pageLen + 1 //如果需要返回首页 // 获取页面所在栈位置 const page = u => { //获取指定页面所在数 for (let i = 0; i < pageStack.length; i++) { if (pageStack[i].route === u) { return i } } } const pa1 = page(pageStack[pageLen - 1].route) // 当前页数 const pa2 = page(url) // 需要跳转的页 //实际需要跳转的页数 const pageNum = pa1 - pa2 if (pageNum >= 0) { uni.navigateBack({ delta: pageNum }) return true } if (type === 'navigate') { uni.navigateTo({ url: '/' + url }) return true } if (type === 'redirect') { uni.redirectTo({ url: '/' + url }) return true } } 使用很简单,你直接传入对应的下一个页面的地址就行了,注意不要有前面的‘/’ 这样页面在跳转的时候就会优先找历史中缓存的页面,没有的情况下才会新开页面。有效避免了10层最大缓存问题,并且现在流畅性提高一个档次(因为会复用缓存)。 三、小程序模拟multipart/form-data数据提交和post大数据引发的问题这个不是性能问题,属于个人对于小程序框架的bug踩坑 1、首先小程序一般是不支持formdata方式的,我们改造之后变成可以做到 header部分 header: { 'content-type': 'multipart/form-data; boundary=XXX' }, data传参部分用这个函数处理一下 // 修改为formdata getformdata(obj = {}) { let result = '' for (let name of Object.keys(obj)) { let value = obj[name]; result += '\r\n--XXX' + '\r\nContent-Disposition: form-data; name=\"' + name + '\"' + '\r\n' + '\r\n' + value } return result + '\r\n--XXX--' } 这个当初要这样做是因为旷视的人脸识别接口需要base64数据并且需要是formdata方式进行上传 2、引发的小程序真机调试的bug 当post里面的data数据超过100kb会直接无法请求接口数据,导致接口函数直接没有任何执行效果。万分注意该问题 四、图片压缩问题除了小程序官方提供的图片压缩函数wx.compressImage,我们还可以用canvas进行图片压缩,同时我比较不推荐官方的wx.compressImage,因为这个api会导致相册新增图片。像我之前的人脸识别频繁拍照的,那真是灾难。多出来几千张照片 函数呢写好了, async compress({ w, h, canvaId, quality = 0.1, src = '' }) { const ctx = uni.createCanvasContext('compress') const [, img] = await uni.getImageInfo({ src }) const { path, width, height } = img ctx.drawImage(path, 0, 0, width, height, 0, 0, w, h) //描述图片到画布上 return new Promise((reslove, reject) => { ctx.draw(false, async () => { const [err, nimg] = await uni.canvasToTempFilePath({ fileType: 'jpg', canvasId: 'compress', quality: quality }) if (err) { reject(err) return false } else { reslove(nimg.tempFilePath) } }) }) } 使用 const img = await this.compress({ w: windowWidth, // 希望的图片宽高 h: windowHeight, canvaId: 'compress', quality: 0.5, // 压缩质量 src: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1602222973914&di=80f148ded356938570ed00fe9f68b816&imgtype=0&src=http%3A%2F%2Fwww.deskcar.com%2Fdesktop%2Ffengjing%2F2007125102851%2F10.jpg' }) 注意html部分 <canvas class="compress" canvas-id="compress"></canvas> 这里我提供的函数没有对canvas的图片的宽高动态变化,这个大家注意下。在getImageInfo这个接口之后注意要给canvas赋值下图片本身的宽高。避免矿高比变形。 五、图片预加载这个其实就比较常用,特别是我们这边有几个比较大的前置图片,如果进入页面再加载那么背景图就无法里面加载好。 这里用 uni.getImageInfo({ src: ‘’ })就可以了,注意看下官方的api,是在success之后会得到一个本地的临时图片地址,我们拿这个地址去全局使用就行了 官方文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html 六、ios和安卓多个视频交替切换,会黑一下的问题这个原因是因为视频播放之前需要进行解码,而解码是需要时间的,那么在这个之前,播放肯定是黑的。安卓还好,ios非常严重。产品大大说这个必须要改 1、我们这个是多个视频切换,那么我们增加背景视频或者背景图片避免黑屏尴尬 2、但是这样我们如果只是监听onplay是不行的,还是会比较明显的黑一下 并且这里引出一个bug,当多个视频之间相互切换的时候,上一个视频帧数据会影响下一个视频帧数据。如果是单纯的去监听api也是没有用的。 最后我的代码是这样的。做到了无缝切换的感觉 export default { data() { return { isplay: false, // 是否播放了 show: '', // 播放的视频地址 } }, created() { // 这里定义的变量不会参与渲染 this.check = false // 当前是否切换地址了 this.index = 0 this.src = [ ‘公司的视频,自己换’, ‘公司的视频,自己换’, ‘公司的视频,自己换’, ‘公司的视频,自己换’, ] }, methods: { next() { this.isplay = false this.show = '' if (this.index === this.src.length - 1) { this.index = 0 } // 模拟接口请求,500毫秒延迟 setTimeout(() => { this.show = this.src[this.index] //+ '?t=' + new Date().getTime() this.index = this.index + 1 this.check = true }, 500) }, onended() { this.isplay = false }, //监听视频播放时间更新 ontimeupdate(e) { const currentTime = e.detail.currentTime // 排除干扰因素 if (currentTime <= 0) return false // 只有当前未播放,切换视频地址情况下,并且时间开始为零点几才可以撤销遮罩视频 // 原理:多个视频切换播放的时候上一个视频播放帧会因为卡顿等问题延迟到下一个视频播放 if (!this.isplay && this.check && currentTime.toString().substring(0, 1) === '0') { this.isplay = true this.check = false } } } } 代码为uniapp的代码,但是我已经大幅简化了,拿去试一试吧 摸鱼结束吐槽下掘金的文章编辑器感觉越来越难用。代码块赋值黏贴没格式,然后放过来之后呢文章的格式全部不对。搞得我需要先放语雀里面重新编辑下在贴回来
2020-10-10 - 复杂瀑布流长列表页踩坑记录,内存不足问题【2】
第二期来啦,带来了新的方案和代码片段~ 第一期点此查看 上期问题 经过一系列的实践,上期的方案有些问题,其中最麻烦的就是,需要对外传递一个当前index,然后控制前后数据展示;这里对于每个用到[代码]skeleton[代码]组件的页面来说,都要重复的写一个方法来承接这个index,然后渲染页面对应的数据。 优化 依然是监听[代码]skeleton[代码]曝光,这里监听的方案变为出现在屏幕上下[代码]n[代码]屏的内容块进行展示,此范围外的内容块就卸载掉。 核心代码 [代码] // 修改了监听是否显示内容的方法,改为前后showNum屏高度渲染 // 监听进入屏幕的范围relativeToViewport({top: xxx, bottom: xxx}) let info = SystemInfo.getInfo() //获取系统信息 let { windowHeight = 667 } = info.source.system let showNum = 2 //超过屏幕的数量,目前这个设置是上下2屏 let listItemContainer = this.createIntersectionObserver() listItemContainer.relativeToViewport({ top: showNum * windowHeight, bottom: showNum * windowHeight }) .observe(`#list-item-${this.data.skeletonId}`, (res) => { // 此处来控制slot展示,详见代码片段 }) [代码] 干货 话不多说,干货放后面,点击获取代码片段
2019-12-05 - [有点炫]自定义navigate+分包+自定义tabbar
自定义navigate+分包+自定义tabbar,有需要的可以拿去用用,可能会存在一些问题,根据自己的业务改改吧 大家也可以多多交流 代码片段:在这里 {"version":"1.1.5","update":[{"title":"修复 [复制代码片段提示] 无法使用的问题","date":"2020-06-15 09:20","imgs":[]}]} 更新日志: 2019-11-25 自定义navigate 也可以调用wx.showNavigationBarLoading 和 wx.hideNavigationBarLoading 2019-11-25 页面滚动条显示在自定义navigate 和 自定义tabbar上面的问题(点击“体验custom Tabbar” [图片] [图片] 其他demo: 云开发之微信支付:代码片段
2020-06-15 - 极致的scroll-view的下拉刷新扩展组件
不敢说是最好的,但是感觉也应该是性能和体验比较极致的下拉刷新扩展了,老规矩,代码片段放最后了~ 2020.2.22 修复了小程序基础库v2.10.2带来的不能滚动的问题,最新代码片段见scroll-view-extends 原理 其实原理很简单,和普通H5以及市面上有的下拉刷新没有特别大的区别,都是基于[代码]touch[代码]手势检测事件来实现下拉刷新的。[代码]touchstart[代码]的时候记录当前触摸点,[代码]touchmove[代码]的时候开始计算移动方向和移动距离, [代码]touchend[代码]的时候计算是否要进行下拉刷新操作。如图所示: [图片] 实现方法 调研了一些实现方法,目前大部分都是通过js计算,然后setData来改变元素的[代码]transform[代码]值实现下拉刷新。考虑到性能问题,此处使用了[代码]wxs[代码]的响应式能力来实现整个计算逻辑,不用通过逻辑层和视图层通信,直接在视图层进行渲染。具体文档请参考wxs响应事件。 这里在[代码]list[代码]组件(由[代码]scroll-view[代码]组成)下抽出了一个[代码]scroll.wxs[代码]作为响应事件的事件处理函数集合,源码基本上就在[代码]scroll.wxs[代码]和[代码]list[代码]组件。 [代码]scroll.wxs[代码]定义了如下变量和函数: [代码]var moveStartPosition = 0 //开始位置 var moveDistance = 0 //移动距离 var moveRefreshDistance = 60 //达到刷新的阈值 var moveMaxDistance = 100 //最大可滑动距离 var isRefreshMaxDown = false //是否达到了最大距离, 用来判断是否要震动提示 var loading = false //是否正在loading ... ... module.exports = { touchStart: touchStart, //手指开始触摸事件 touchMove: touchMove, //手指移动事件 touchEnd: touchEnd, //手指离开屏幕事件 loadingTypeChange: loadingTypeChange, //请求状态变化监听,监听刷新请求开始和请求完成 triggerRefresh: triggerRefresh //主动触发刷新操作,比如点击页面上一个按钮,重新刷新list,这就需要用到这个方法 } [代码] [代码]touchStart[代码]和[代码]touchMove[代码]就不用说了,代码注释都很明白,普通的监听移动和处理逻辑。 [代码]touchEnd[代码]主要是判断移动距离是否达到了阈值,然后根据结果,调用监听实例的[代码]callMethod[代码]方法触发[代码]refreshStart[代码]或者[代码]refreshCancel[代码]方法,这两个方法都是写到[代码]list[代码]组件里面的,用来触发刷新方法或者取消刷新。 [代码]loadingTypeChange[代码]方法主要是监听刷新是否完成,以此来触发动画效果。 [代码]triggerRefresh[代码]通过监听主动触发的变量来处理。如果需要主动触发刷新,则调用[代码]list[代码]组件内部的[代码]forceRefresh[代码]方法,具体使用示例在[代码]index/index/js[代码]的[代码]onLoad[代码]函数有: [代码]this.selectComponent('.list').forceRefresh()[代码] [代码]scroll.wxs[代码]里面还有一个未导出的方法,叫[代码]drawTransitionY[代码],这个方法主要是因为[代码]ios12[代码]对于[代码]transition[代码]动画效果支持的不好,所以自己写了个Y轴方向的动画([代码]linear[代码]线性的),大佬们可以自己往上添加各种[代码]ease-in-out[代码]效果。 里面具体的实现可以查看代码注释哦~ 使用 好了,前面讲了实现的原理和方法,那么在代码里面,应该怎么直接使用呢?如下代码所示: [代码]<!-- 使用示例 --> <list class="list" refresh-loading="{{refreshLoading}}" loading="{{loading}}" bindrefresh="initList" bindloadmore="loadmore"> <!-- your code --> </list> [代码] [代码]refresh-loading[代码]属性用来通过外部loading态来控制刷新动画的开始结束,因为每当变化[代码]refresh-loading[代码]的值时,会将变化同步到组件内的[代码]showRefresh[代码]属性,[代码]wxs[代码]通过监听[代码]showRefresh[代码]来处理动画逻辑。 [代码]loading[代码]属性是上拉加载更多的时候触发的loading态展示,跟刷新无关 [代码]bindrefresh[代码]是刷新触发时绑定的函数,下拉刷新动画成功开始后触发这个函数 [代码]bindloadmore[代码]透传[代码]scroll-view[代码]的加载更多方法 当然,源码里面也包含了一个[代码]list-item[代码]组件,这个跟本文没太大关系,是用来做瀑布流长列表内容太多时的内存不足问题解决方案的,具体请看解决小程序渲染复杂长列表,内存不足问题 干货 最后,上代码片段, 小程序代码片段 github地址
2020-02-22 - 2020-08-16
- 爸妈搜日历
提供简约不简单的日历基本功能,自定义样式,考勤状态等功能。
2018-10-08