react-infinite-scroller
就是一个组件,重要逻辑就是addEventListener
绑定scroll
事宜。
看它的源码重要意义不在晓得怎样运用它,而是晓得今后处置惩罚转动加载
要注重的东西。
此处跳到
总结。
初识
参数:
// 衬着出来的DOM元素name
element: 'div',
// 是不是能继承转动衬着
hasMore: false,
// 是不是在定阅事宜的时刻实行事宜
initialLoad: true,
// 示意当前翻页的值(每衬着一次递增)
pageStart: 0,
// 通报ref,返回此组件衬着的 DOM
ref: null,
// 触发衬着的间隔
threshold: 250,
// 是不是在window上绑定和处置惩罚间隔
useWindow: true,
// 是不是反向转动,即到顶端后衬着
isReverse: false,
// 是不是运用捕捉形式
useCapture: false,
// 衬着前的loading组件
loader: null,
// 自定义转动组件的父元素
getScrollParent: null,
深切
componentDidMount
componentDidMount() {
this.pageLoaded = this.props.pageStart;
this.attachScrollListener();
}
实行attachScrollListener
attachScrollListener
attachScrollListener() {
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
猎取父组件(用户自定义父组件或许当前dom的parentNode)
然后绑定了3个事宜,分别是scroll
,resize
,mousewheel
前2种都绑定scrollListener
,mousewheel
是一个非标准事宜,是不发起在临盆形式中运用的。
那末这里为何要运用呢?
mousewheel处理chrome的守候bug
此处的mousewheel
事宜是为了处置惩罚chrome
浏览器的一个特征(不晓得是不是是一种bug)。
上面这个题目重要形貌,当在运用滚轮加载,而且加载会触发ajax要求
的时刻,当滚轮抵达底部,会涌现一个冗长而且无任何行动的守候(长达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
,很好明白,运用用户自定义的父组件,或许当前组件DOM.parentNode
。
scrollListener
scrollListener() {
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;
}
// 此处应该要推断确保转动组件一般显现
if (
offset < Number(this.props.threshold) &&
(el && el.offsetParent !== null)
) {
// 卸载事宜
this.detachScrollListener();
// 卸载事宜后再实行 loadMore
if (typeof this.props.loadMore === 'function') {
this.props.loadMore((this.pageLoaded += 1));
}
}
}
组件中心。
几个进修/温习点
offsetParent
offsetParent
返回一个指向近来的包括该元素的定位元素.offsetParent
很有效,由于盘算offsetTop
和offsetLeft
都是相对于offsetParent
边境的。ele.offsetParent
为 null 的3种状况:- ele 为
body
- ele 的
position
为fixed
ele 的
display
为none
此组件中
offsetParent
处置惩罚了2种状况在
useWindow
的状况下(即事宜绑定在window,转行动用在body)经由过程递归猎取
offsetParent
抵达顶端的高度(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) ) {/* ... */ }
- ele 为
-
scrollHeight
和clientHeight
在无转动的状况下,
scrollHeight
和clientHeight
相称,都为height
+padding
*2在有转动的状况下,
scrollHeight
示意现实内容高度,clientHeight
示意视口高度。 - 每次实行
loadMore
前卸载事宜。确保不会反复(过量)实行
loadMore
,由于先卸载事宜再实行loadMore
,能够确保在实行过程当中,scroll
事宜是无效的,然后再每次componentDidUpdate
的时刻从新绑定事宜。
render
render() {
// 猎取porps
const renderProps = this.filterProps(this.props);
const {
children,
element,
hasMore,
initialLoad,
isReverse,
loader,
loadMore,
pageStart,
ref,
threshold,
useCapture,
useWindow,
getScrollParent,
...props
} = renderProps;
// 定义一个ref
// 能将当前组件的DOM传出去
props.ref = node => {
this.scrollComponent = node;
// 实行父组件传来的ref(如果有)
if (ref) {
ref(node);
}
};
const childrenArray = [children];
// 实行loader
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
是不允许修正的。
这里运用了解构
getScrollParent,
...props
} = renderProps;
这里解构相当于Object.assign
,定义了一个新的object
,便能够增加属性了,而且this.props
不会受到影响。
总结
react-infinite-scroller
逻辑比较简单。
一些注重/进修/温习点:
-
Chrome
的一个转动加载要求的bug。本文位置 -
offsetParent
的一些现实用法。本文位置 - 经由过程不停定阅和作废事宜绑定让转动实行函数不会频仍触发。本文位置
-
scrollHeight
和clientHeight
区分。本文位置
此库发起运用在自定义的一些组件上而且不那末庞杂的逻辑上。
用在第三方库能够会没法猎取准确的父组件,而经由过程document.getElementBy..
传入。
面临轻微庞杂的逻辑,
比方,一个搜刮组件,定阅onChange
事宜而且显现内容,搜刮”a”,对显现内容转动加载了3次,再增加搜刮词”b”,这时刻”ab”的内容显现是在3次以后。