最近作一個H5項目,數據交互量比較大,不少頁面都是從後臺拿過來數據作一個列表顯示,這天然就遇到了滾動。css
剛開始我直接使用css作法,直接添加overflow: scroll;但在微信端用戶滑動會直接將整個頁面拖動,露出頂部的域名和底部的黑色背景。用戶反映體驗很差,要改……好吧,本身動手。但這並非好改的,由於在React中都是構建的是虛擬DOM,直接操做DOM也會對性能有必定影響。這時候網上搜了一下,拿出一個解決方案,獻出部分代碼:react
import React from 'react'; import { render } from 'react-dom'; import Router from './app/router'; import '../common/css/base.styl'; import '../common/css/waaStyle.styl'; import '../common/css/page.styl'; var divDom = document.createElement('div'); divDom.setAttribute('id', 'wrap'); document.body.appendChild(divDom); document.body.addEventListener("touchmove",function (e) { e.preventDefault(); },false); render(<Router />, divDom);
這是在React的根頁面下直接禁用touchmove事件,禁止用戶touchmove,能夠達到不出現域名和黑色邊框的效果。微信
因爲禁止touchmove事件,在須要列表滾動的地方,overflow:scroll;監聽不到touchmove了,滑動變得無效,怎麼解決呢?咱們能夠在須要滾動的地方禁止冒泡事件:app
//首先要獲取須要禁止冒泡事件的dom,因爲React是構建虛擬的dom,能夠這樣拿到dom:ref= {(ref) => {this.dom = ref}}; componentDidMount () { this.dom.addEventListener("touchmove",function (e) { e.stopPropagation(); },false); } //dom在頁面掛載完成後,禁止冒泡事件。
加上這樣的代碼後,就會發現,整個頁面仍然是禁止滑動,列表頁能夠正常滾動,可是,當列表滾動到底的時候,用戶繼續滑動,整個頁面仍然會跟着滑動,又露出咱們不想看到的部分,感受好氣呀……框架
冷靜分析一下,這條路是走不通的,因爲列表區域禁止了冒泡事件,那麼用戶只要在列表區域滑動,那麼你在body上作的禁止滑動就是沒有效果的!dom
萬般無奈下,我有搬來了個人老夥計:IScroll.js。之因此選他,由於他有一個很是有用的方法:refresh();我只要在componentDidMount中實例化Iscroll,而且在React數據更新後再次refresh就能夠了。那麼開始作了。性能
React框架中引入咱們想要的插件,只要這樣作就行:ui
import Iscroll from "moudle/iscroll/Iscroll";
而後頁面掛載完成後咱們進行實例化this
componentDidMount() { setTimout(function () {//因爲手機性能的緣由,咱們在定時器裏面進行實例化 this.myIScroll = new IScroll("#dom",{ mouseWheel: true, bounce: true, scrollbars: false, }); },10); }
一但React檢測到數據有更新,他就會自動刷新頁面,那麼咱們這個時候須要從新刷新IScroll:spa
componentDidUpdate() { this.myIScroll.refresh(); }
代碼作到這裏,咱們就會發現,即便禁止touchmove事件,咱們依然可使用滾動列表,問題完美解決!不過,此時客戶大手一揮,指着咱們的滾動條,這個滾動條有點醜呀,能不能把它作得科技感一些……哎,數不清的星星,改不完的需求啊,不過,這個對於咱們無(xia)所(chui)不(niu)能(bi)的程序猿也是能夠作得。在IScroll中,它的滾動條是由兩個作了定位的div構成的,那麼
咱們在實例化IScroll的時候參數中設置:
this.myIScroll = new IScroll("#dom",{ scrollbars: 'custom',//即scrollbars的值設置成字符串:「custom」便可 });
而後咱們在樣式表中使用兩個類名:
.iScrollIndicator和.iScrollVerticalScrollbar.iScrollLoneScrollbar寫入樣式,這樣組成滾動條的兩個div就能夠長成咱們但願的樣子啦!
可是根據需求,有的列表很長,須要分頁加載,那麼就須要判斷用戶拖動後是否須要加載下一頁的數據了。
基本的思路是,利用this.y獲得向上移動的距離,用裏面框的高度減去移動的距離,再減去外面框的高度,若是小於某一個高度,咱們去請求數據,以後從新渲染就好了。可是,我查閱了IScroll的相關代碼,文檔上並無寫出內外兩個框高度的接口,也曾經試着本身在源碼裏添加得到框高度的方法,可是在實例化後獲取這個值總會有各類各樣的問題。最後通過仔細研究IScroll源碼,發現外面框的高度就是實例化後的:this.wrapperHeight,而裏面框的高度就是:this.scrollerHeight,這樣咱們減去this.y的絕對值後,就能夠判斷出是否須要加載新的數據了。給大爺們線上代碼:
componentDidMount () { var self = this; const options = { preventDefault: false, zoom: false, mouseWheel: true, probeType: 3, bounce: true, scrollbars: true, }; this.iScrollInstance = new IScroll(this.scrollContent,options); this.iScrollInstance.on('scrollEnd', function() { var shouldGet = this.scrollerHeight + this.y - this.wrapperHeight;//注意this.y必定是非正數,因此這裏是加啦! if (shouldGet < 200 && this.directionY === 1 && !self.state.loading) { //我這裏設置的臨界值是200,即列表滾動結束後,若是裏框的高度還有小於200px的內容沒有顯示過就會去加載新的數據。 if(self.state.pageNo >= self.state.totalPage) { Action.update({noMore: true}); } else { self.state.pageNo ++; Action.getList({pageNo: self.state.pageNo}); } } }); }
作到這裏,咱們利用IScroll想解決的需求均可以知足了……