現在信息流業務是各大互聯網公司爭先搶佔的一個大面包,爲了提升用戶的後續消費,產品想出了各類各樣的方法,例如在微視中,用戶能夠無限上拉出下一條視頻;在知乎中,也能夠無限上拉出下一條回答。這樣的操做方式用戶體驗更好,後續消費也更多。最近幾年的時間,微信小程序已經從一顆小小的萌芽成長爲參天大樹,造成了較大規模的生態,小程序也擁有了一個很大的流量入口。前端
那如何才能在小程序中實現類原生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