摘要:看它的源碼主要意義不在知道如何使用它,而是知道以后處理滾動加載要注意的東西。通過判斷不為的情況,確保滾動組件正常顯示和在無滾動的情況下,和相等,都為在有滾動的情況下,表示實際內(nèi)容高度,表示視口高度。
react-infinite-scroller就是一個組件,主要邏輯就是addEventListener綁定scroll事件。
看它的源碼主要意義不在知道如何使用它,而是知道以后處理滾動加載要注意的東西。
此處跳到總結(jié)。初識
參數(shù):
// 渲染出來的DOM元素name element: "div", // 是否能繼續(xù)滾動渲染 hasMore: false, // 是否在訂閱事件的時候執(zhí)行事件 initialLoad: true, // 表示當(dāng)前翻頁的值(每渲染一次遞增) pageStart: 0, // 傳遞ref,返回此組件渲染的 DOM ref: null, // 觸發(fā)渲染的距離 threshold: 250, // 是否在window上綁定和處理距離 useWindow: true, // 是否反向滾動,即到頂端后渲染 isReverse: false, // 是否使用捕獲模式 useCapture: false, // 渲染前的loading組件 loader: null, // 自定義滾動組件的父元素 getScrollParent: null,深入 componentDidMount
componentDidMount() { this.pageLoaded = this.props.pageStart; this.attachScrollListener(); }
執(zhí)行attachScrollListener
attachScrollListenerattachScrollListener() { const parentElement = this.getParentElement(this.scrollComponent); if (!this.props.hasMore || !parentElement) { return; } let scrollEl = window; if (this.props.useWindow === false) { scrollEl = parentElement; } scrollEl.addEventListener( "mousewheel", this.mousewheelListener, this.props.useCapture, ); scrollEl.addEventListener( "scroll", this.scrollListener, this.props.useCapture, ); scrollEl.addEventListener( "resize", this.scrollListener, this.props.useCapture, ); if (this.props.initialLoad) { this.scrollListener(); } }
此處通過getParentElement獲取父組件(用戶自定義父組件或者當(dāng)前dom的parentNode)
然后綁定了3個事件,分別是scroll,resize,mousewheel
前2種都綁定scrollListener,mousewheel是一個非標(biāo)準(zhǔn)事件,是不建議在生產(chǎn)模式中使用的。
那么這里為什么要使用呢?
mousewheel解決chrome的等待bug此處的mousewheel事件是為了處理chrome瀏覽器的一個特性(不知道是否是一種bug)。
stackoverflow:Chrome的滾動等待問題
上面這個問題主要描述,當(dāng)在使用滾輪加載,而且加載會觸發(fā)ajax請求的時候,當(dāng)滾輪到達(dá)底部,會出現(xiàn)一個漫長而且無任何動作的等待(長達(dá)2-3s)。
window.addEventListener("mousewheel", (e) => { if (e.deltaY === 1) { e.preventDefault() } })
以上綁定可以消除這個"bug"。
個人并沒有遇到過這種情況,不知道是否有遇到過可以說說解決方案。getParentElement
getParentElement(el) { const scrollParent = this.props.getScrollParent && this.props.getScrollParent(); if (scrollParent != null) { return scrollParent; } return el && el.parentNode; }
上面用到了getParentElement,很好理解,使用用戶自定義的父組件,或者當(dāng)前組件DOM.parentNode。
scrollListenerscrollListener() { const el = this.scrollComponent; const scrollEl = window; const parentNode = this.getParentElement(el); let offset; // 使用window的情況 if (this.props.useWindow) { const doc = document.documentElement || document.body.parentNode || document.body; const scrollTop = scrollEl.pageYOffset !== undefined ? scrollEl.pageYOffset : doc.scrollTop; // isReverse指 滾動到頂端,load新組件 if (this.props.isReverse) { // 相反模式獲取到頂端距離 offset = scrollTop; } else { // 正常模式則獲取到底端距離 offset = this.calculateOffset(el, scrollTop); } // 不使用window的情況 } else if (this.props.isReverse) { // 相反模式組件到頂端的距離 offset = parentNode.scrollTop; } else { // 正常模式組件到底端的距離 offset = el.scrollHeight - parentNode.scrollTop - parentNode.clientHeight; } // 此處應(yīng)該要判斷確保滾動組件正常顯示 if ( offset < Number(this.props.threshold) && (el && el.offsetParent !== null) ) { // 卸載事件 this.detachScrollListener(); // 卸載事件后再執(zhí)行 loadMore if (typeof this.props.loadMore === "function") { this.props.loadMore((this.pageLoaded += 1)); } } }
組件核心。
幾個學(xué)習(xí)/復(fù)習(xí)點
offsetParent
offsetParent返回一個指向最近的包含該元素的定位元素.
offsetParent很有用,因為計算offsetTop和offsetLeft都是相對于offsetParent邊界的。
ele.offsetParent為 null 的3種情況:
ele 為body
ele 的position為fixed
ele 的display為none
此組件中offsetParent處理了2種情況
在useWindow的情況下(即事件綁定在window,滾動作用在body)
通過遞歸獲取offsetParent到達(dá)頂端的高度(offsetTop)。
calculateTopPosition(el) { if (!el) { return 0; } return el.offsetTop + this.calculateTopPosition(el.offsetParent); }
通過判斷offsetParent不為null的情況,確保滾動組件正常顯示
if ( offset < Number(this.props.threshold) && (el && el.offsetParent !== null) ) {/* ... */ }
scrollHeight和clientHeight
在無滾動的情況下,scrollHeight和clientHeight相等,都為height+padding*2
在有滾動的情況下,scrollHeight表示實際內(nèi)容高度,clientHeight表示視口高度。
每次執(zhí)行loadMore前卸載事件。
確保不會重復(fù)(過多)執(zhí)行loadMore,因為先卸載事件再執(zhí)行loadMore,可以確保在執(zhí)行過程中,scroll事件是無效的,然后再每次componentDidUpdate的時候重新綁定事件。
renderrender() { // 獲取porps const renderProps = this.filterProps(this.props); const { children, element, hasMore, initialLoad, isReverse, loader, loadMore, pageStart, ref, threshold, useCapture, useWindow, getScrollParent, ...props } = renderProps; // 定義一個ref // 能將當(dāng)前組件的DOM傳出去 props.ref = node => { this.scrollComponent = node; // 執(zhí)行父組件傳來的ref(如果有) if (ref) { ref(node); } }; const childrenArray = [children]; // 執(zhí)行l(wèi)oader if (hasMore) { if (loader) { isReverse ? childrenArray.unshift(loader) : childrenArray.push(loader); } else if (this.defaultLoader) { isReverse ? childrenArray.unshift(this.defaultLoader) : childrenArray.push(this.defaultLoader); } } // ref 傳遞給 "div"元素 return React.createElement(element, props, childrenArray); }
這里一個小亮點就是,在react中,this.props是不允許修改的。
這里使用了解構(gòu)
getScrollParent, ...props } = renderProps;
這里解構(gòu)相當(dāng)于Object.assign,定義了一個新的object,便可以添加屬性了,并且this.props不會受到影響。
總結(jié)react-infinite-scroller邏輯比較簡單。
一些注意/學(xué)習(xí)/復(fù)習(xí)點:
Chrome的一個滾動加載請求的bug。本文位置
offsetParent的一些實際用法。本文位置
通過不斷訂閱和取消事件綁定讓滾動執(zhí)行函數(shù)不會頻繁觸發(fā)。本文位置
scrollHeight和clientHeight區(qū)別。本文位置
此庫建議使用在自定義的一些組件上并且不那么復(fù)雜的邏輯上。
用在第三方庫可以會無法獲取正確的父組件,而通過document.getElementBy..傳入。
面對稍微復(fù)雜的邏輯,
例如,一個搜索組件,訂閱onChange事件并且呈現(xiàn)內(nèi)容,搜索"a",對呈現(xiàn)內(nèi)容滾動加載了3次,再添加搜索詞"b",這時候"ab"的內(nèi)容呈現(xiàn)是在3次之后。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99394.html
摘要:描述最近在用框架寫一個項目遇到了一個小問題列表會加載出很多數(shù)據(jù)需要在固定區(qū)域查看所有的列表數(shù)據(jù)需求給列表增加一個滑動框開始在官網(wǎng)上看了下例子比較復(fù)雜是結(jié)合實現(xiàn)滾動自動加載列表。 描述:最近在用Ant Design 框架寫一個項目,遇到了一個小問題,list列表會加載出很多數(shù)據(jù).需要在固定區(qū)域查看所有的列表數(shù)據(jù).需求:給list列表增加一個滑動框 開始在官網(wǎng)上看了下例子.比較復(fù)雜..是l...
摘要:下滾動到頁面底部無限加載數(shù)據(jù)看到一篇覺得挺實用的就看了下順便簡單翻譯了一下給需要的人參考從這個項目中可以加深對的生命周期的理解何時開始請求如何結(jié)合使用原生來寫事件等等我這里主要是對原文的重點提取和補(bǔ)充本文技術(shù)要點生命周期簡單用法格式化日期圖 Vue下滾動到頁面底部無限加載數(shù)據(jù)Demo 看到一篇Implementing an Infinite Scroll with Vue.js, 覺得...
閱讀 2468·2021-11-19 09:40
閱讀 3601·2021-11-17 17:08
閱讀 3807·2021-09-10 10:50
閱讀 2229·2019-08-27 10:56
閱讀 1953·2019-08-27 10:55
閱讀 2649·2019-08-26 12:14
閱讀 1002·2019-08-26 11:58
閱讀 1501·2019-08-26 10:43