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