在你漫长的职业生涯中,你一定会遇到这样一种代码问题:你费尽毕生功能,搞断点、打 log,抓包,google... 各种高端操作轮番上阵,使尽浑身解数,然后,你,依然解决不了它。
这种问题,我最近,又双叒叕遇到了... 折腾了半天,底牌尽出,最后,被逼无奈,我直接掏出了压箱底的绝招,一招秒杀,直接搞定。这么好用的方式,必须推荐给大家试试。
本文共 3014 字,阅读预计花费 5 分钟
1
页面切换卡顿
最近发布了我的一款个人小程序前端码易,结果刚一发布,粉丝朋友们就反馈了一大堆问题。其中有一个性能问题把我搞得很麻。
有粉丝在使用过程中发现部分页面的切换动画非常卡顿不流畅。最诡异的是,就那几个页面动画卡顿,其他页面又非常流畅。
✓页面切换是指从一个页面,跳转到另外一个页面
那我一想,肯定是我的代码有问题啊。所以就去反复查看自己对应几个页面的代码。
可结果却让我很郁闷啊,凭借我十多年的深厚开发功底,学贯前后端的高端技术专家,知命境大成的顶尖修为,翻来覆去看了几遍,居然完全没发现哪有什么问题 ~
无从下手 ~
有点丢人... 自诩资深搬砖,居然一点头绪都没有。不过我依然不动声色。因为我还有底牌未出。这个时机恰恰合适,传给各位道友一个八字真言:
谷歌在手,天下我有!
想好关键词:小程序页面切换卡顿。输入,搞...
定个鬼... 万万没想到,我都换 google 了, 万恶的 CSDN 还缠着我不放 (*  ̄︿ ̄) 。
但是,怀抱着一点希望的我还是再次选择相信。但愿能找到一个雷同的场景,把问题解决掉。点进去,仔细查看文章内容,果然没有惊喜...
微信小程序开发者社区,也不太靠谱,经常提问的一大堆,认真回答的没几个,官方开发团队在这方面也不是很积极。
翻了好几篇文章,终于找了一种说法,可以在全局样式中新增如下属性来解决切换动画卡顿的问题。
page {
-webkit-overflow-scrolling: touch;
}
病急乱投医试了一下,很可惜没有效果。后来想起来了这条属性是用来解决 iOS 使用 overflow: scroll
滚动不流畅的问题的。
谷歌这条路走不通!
不慌,马上切换战术,祭出性能优化神器 Performance。想必各位道友对此也非常熟悉,对其的掌握肯定也是炉火纯青。点击这个按钮,先来一手记录。
然后我们就去操作页面切换,操作结束后点击 stop 按钮。
然后我们就会得到一张可操作交互的火焰图。如果我们发现哪一块地方被标记为红色了,那么就可能存在问题。
由于操作时间偏长,所以得到的火焰图比较细,看不出什么,我们通过鼠标缩短选中时间,然后下方的火焰图就会变大,变大,变大...
可是,看了半天,试了几次,火焰图表现都挺良好的,也没问题啊... ,硕大一张火焰图,连个线索都不愿意给我提供
这个时候,我就有点慌了呀。底牌出完了... 打断点?也不合适啊,功能没问题,只是一个切换动画卡顿而已
我就想不通了。小程序出来了都快十年了,难道就我遇到了这个问题?别人没遇到?还是大家都这么强,不认为这个问题很难解决?抬抬手就搞定了?压根没有放到网上讨论的必要?
一时之间,我都有点怀疑是不是自己犯了什么低级错误...
然后又像个傻 X 一样去把代码从头到尾看了 1,2,3 遍...
代码看上去,确实没什么问题。
看来,我只能,献祭灵魂,燃烧精血,付出巨大代价,用出我最离谱的一招必杀技:删除定位法
✓删除代码定位法:
1、先删除全部逻辑代码,只留下最简单的页面基础功能,验证页面卡顿是否存在
2、确定删除之后,问题消失。再删除部分代码,逐一排查,执行页面,查看卡顿是否消失
3、如果没有,则继续删除其他部分代码,直到找到问题代码为止
你以为就是简单的删除?不,此法可以暗藏玄机。为了快速定位到问题代码所在,我运用了二分查找法。第二次删除时,我直接删掉上面一半代码,只需要确保留下的代码不报错即可。然后运行验证,我就可以锁定造成卡顿的代码在哪个区间。以同样的方式重复执行,每次都能减少 50% 的搜索区域,直到最后定位到问题代码。
此法离谱确实是离谱,骚也是真骚。它在我的十多年职业生涯中,所向披靡,战功显赫,此法一出,bug 无不闻风丧胆。
果不其然,我最终快速找到了问题所在。
在该页面组件中,我写了这样一段代码
useEffect(() => {
ad.current = Taro.createRewardedVideoAd({
adUnitId: ads.ad15
})
...
}, [])
在微信小程序中,createRewardedVideoAd
方法是用来提前创建激励广告实例,以便于读者在点击按钮观看广告时,广告已经创建好了可以直接播放,而不用等待那么久。
万万没想到,该方法居然是一个影响渲染的耗时操作。页面如下
这里一个很具有迷惑性的地方在于,实际上我调用该方法的时机,已经在组件创建完成之后了。所以按照我之前的逻辑,这里不应该会造成卡顿才对。
useEffect(() => {
// 组件创建完成之后
}, [])
因此,我就有点懵了,为什么组件创建完成之后调用该方法,还是会卡顿呢?
2
小程序页面切换渲染流程
思虑良久,我从结果反推之后,结合小程序的页面切换渲染流程,才想明白问题是如何导致的。
微信小程序页面切换的详细逻辑应该是
1、在当前页面点击按钮
2、创建新页面实例
3、创建完成之后执行入场动画
在这个流程之下我们可以猜想出两种造成页面卡顿的可能。
这种情况的表现为,点击按钮有延迟感,响应不及时。
- 2、执行入场动画时,执行了耗时任务,跟主线程抢占执行资源
这种情况的表现为,入场动画渲染掉帧,不流畅,卡顿。
3
解决方案
定位到原因,并想明白整个渲染过程之后,解决的方案就非常简单了。我分析了我的几个卡顿的页面,发现上面两种情况都有存在。
先来解决切换动画卡顿的页面。
解决思路就是我们只需要在入场动画执行结束之后,再执行耗时任务即可。微信小程序并没有给开发者提供页面切换动画的具体结束钩子,但是,我们可以使用定时器来推后耗时任务的执行。在定时器中设定一个大概差不多的时间即可。
useEffect(() => {
setTimeout(() => {
ad.current = Taro.createRewardedVideoAd({
adUnitId: ads.ad15
})
}, 200)
...
}, [])
再保存,重试,发现卡顿的页面流畅了!!!完美解决。
我又分析了另外几个页面,发现还存在另外一种情况的卡顿。
当我点击按钮之后,过了很长时间切换动画才开始执行。
通常情况下,造成这种卡顿的原因是因为页面初始化时执行的逻辑过多,或者渲染的内容多过,导致初始化时间过长,从而造成反应缓慢。解决的办法就是通过懒加载延后处理和渲染非首屏内容。
在 React 的项目中,我可以设计一个状态用来阻止大量页面内容在初始化时渲染,先让他渲染主要内容。然后在组件首次渲染完成之后,将该状态设置为 true。当然为了确保内容的渲染不影响动画的执行,我们还可以继续延后 100ms.
一顿操作,调整代码如下
export default function Star() {
const [show, setShow] = useState(false)
useEffect(() => {
setTimeout(() => {
setShow(true)
}, 100)
}, [])
if (!show) {
return <div>div>
}
return (
<div>这里有四百多个元素div>
)
}
保存。运行。卡顿的感觉终于全部消失了!!!
给力!