现在信息流业务是各大互联网公司争先抢占的一个大面包,为了提升用户的后续消费,产品想出了各类各样的方法,例如在微视中,用户能够无限上拉出下一条视频;在知乎中,也能够无限上拉出下一条回答。这样的操做方式用户体验更好,后续消费也更多。最近几年的时间,微信小程序已经从一颗小小的萌芽成长为参天大树,造成了较大规模的生态,小程序也拥有了一个很大的流量入口。前端
那如何才能在小程序中实现类原生APP效果的下一条无限刷体验?web
这篇文章详细记录了下一条无限刷效果的实现原理,以及细节和体验优化,并将相关代码抽象成一个微信小程序代码片断,有须要的同窗可查看demo源码。小程序
线上效果请用微信扫码体验:微信小程序
小程序demo体验请点击:https://developers.weixin.qq.com/s/vIfPUomP7f9a数组
出于性能和兼容性考虑,咱们尽可能采用小程序官方提供的原生组件来实现下一条无限刷效果。咱们发现,能够将无限上拉下一篇的文章看做一个竖向滚动的轮播图,又因为每一篇文章的内容长度高于一屏幕高度,因此须要实现文章内部可滚动,以及文章之间能够上拉和下拉切换的功能。性能优化
在屡次尝试后,咱们最终采用了在<swiper>
组件内部嵌套一个<scroll-view>
组件的方式实现,利用<swiper>
组件来实现文章之间上拉和下拉切换的功能,利用<scroll-view>
来实现一篇文章内部可上下滚动的功能。微信
因此页面的dom结构以下所示:架构
<swiper class='scroll-swiper' circular="{{false}}" vertical="{{true}}" bindchange="bindChange" skip-hidden-item-layout="{{true}}" duration="{{500}}" easing-function="easeInCubic" > <block wx:for="{{articleData}}"> <swiper-item> <scroll-view scroll-top="0" scroll-with-animation="{{false}}" scroll-y > content </scroll-view> </swiper-item> </block> </swiper>
咱们知道view部分是运行在webview上的,因此前端领域的大多数优化方式都有用。例如减小代码包体积,使用分包,渲染性能优化等。下面主要讲一下渲染性能优化。dom
因为页面须要无限上拉刷新,因此要在<swiper>
组件中不断的增长<swiper-item>
,这样必然会致使页面的dom节点成倍数的增长,最后很是卡顿。性能
为了优化页面的dom节点,咱们利用<swiper>
的current
和<swiper-item>
的index
来作优化,控制是否渲染dom节点。首先,仅当index <= current + 1
时渲染<swiper-item>
,也就是页面中最多预先加载出下一条,而不是将接口返回的全部后续数据都渲染出来;其次,对于用户已经消费过的以前的<swiper-item>
,不能直接销毁dom节点,不然会致使<swiper>
的current
值出现错乱,可是咱们能够控制是否渲染<swiper-item>
内部的子节点,咱们设置了仅当current <= index + 1 && index -1 <= current
时才会渲染<swiper-item>
中的内容,也就是仅渲染当先文章,及上一篇和下一篇的文章内容,其余文章的dom节点都被销毁了。
这样,不管用户上拉刷新了多少次,页面中最多只会渲染3篇文章的内容,避免了由于上拉次数太多致使的页面卡顿。
小程序的视图层目前使用WebView
做为渲染载体,而逻辑层是由独立的 JavascriptCore
做为运行环境。在架构上,WebView
和 JavascriptCore
都是独立的模块,并不具有数据直接共享的通道。当前,视图层和逻辑层的数据传输,实际上经过两边提供的 evaluateJavascript
所实现。即用户传输的数据,须要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 JS
脚本,再经过执行 JS
脚本的形式传递到两边独立环境。
而 evaluateJavascript
的执行会受不少方面的影响,数据到达视图层并非实时的。
setData
的调用都是一次进程间通讯过程,通讯开销与 setData 的数据量正相关。setData
会引起视图层页面内容的更新,这一耗时操做必定时间中会阻塞用户交互。setData
是小程序开发中使用最频繁的接口,也是最容易引起性能问题的接口。data
应仅包括与页面渲染相关的数据,其余数据可绑定在this上。使用 data
在方法间共享数据,会增长 setData 传输的数据量,。setData
传输大量数据,通信耗时与数据正相关,页面更新延迟可能形成页面更新开销增长。仅传输页面中发生变化的数据,使用 setData
的特殊 key
实现局部更新。setData
,避免短期内频繁调用 setData
,对连续的setData调用进行合并。否则会致使操做卡顿,交互延迟,阻塞通讯,页面渲染延迟。setData
,这样会抢占前台页面的渲染资源。可将页面切入后台后的setData
调用延迟到页面从新展现时执行。无限上拉刷新的数据会采用分页接口的形式,分屡次请求回来。在使用分页接口拉取到下一刷的数据后,咱们须要调用setData
将数据写进data
的articleData
中,这个articleData
是一个数组,里面存放着全部的文章数据,数据量十分庞大,若是直接setData
会增长通信耗时和页面更新开销,致使操做卡顿,交互延迟。
为了不这个问题,咱们将articleData
改进为一个二维数组,每一次setData
经过分页的 cachedCount
标识来实现局部更新,具体代码以下:
this.setData({ [`articleData[${cachedCount}]`]: [...data], cachedCount: cachedCount + 1, })
articleData
的结构以下:
解决了操做卡顿,交互延迟等问题,咱们还须要对动画和交互的体验进行优化,以达到类原生APP效果的体验。
在文章间上拉切换时,咱们使用了<swiper>
组件自带的动画效果,并经过设置duration
和easing-function
来优化滚动细节和动画。
当用户阅读文章到底部时,会提示下一篇文章的标题等信息,而在页面上拉时,因为下一篇文章的内容已经加载出来了,这样在滑动过程当中会出现两个重复的标题。为了不这种状况出现,咱们经过一个占满屏幕宽高的空白<view>
来将下一篇文章的内容撑出屏幕,并在滚动结束时,经过hidden="{{index !== current && index !== current + 1}}"
来隐藏这个空白<view>
,并对这个空白<view>
的高度变化增长动画,来实现下一篇文章从屏幕底部滚动到屏幕顶部的效果:
.fake-scroll { height: 100%; width: 100%; transition: height 0.3s cubic-bezier(0.167,0.167,0.4,1); }
而当用户想要上拉查看以前阅读过的文章时,咱们须要给用户一个“下滑查看上一条”提示,因此也能够采用同上的方式,经过一个占满屏幕宽高的提示语<view>
来将上一篇文章的内容撑出屏幕,并在滚动结束时,经过wx:if="{{index + 1 === current}}"
来隐藏这个提示语<view>
,并对这个提示语<view>
的透明度变化增长动画,来实现下拉时提示“下滑查看上一条”的效果:
.fake-previous { height: 100%; width: 100%; opacity: 0; transition: opacity 1s ease-in; } .fake-previous.show-fake-previous { opacity: 1; }
至此,这个类原生APP效果的下一条无限刷体验的需求的全部要点和细节都已实现。
记录在此,欢迎交流和讨论。
小程序demo体验请点击:https://developers.weixin.qq.com/s/vIfPUomP7f9a