当页面数据过多为了优化页面加载速度和避免页面的卡顿时使用滚动加载的方式实现数据的分页加载功能
加载更多方案 通过监听对应元素的滚动,不断计算元素相对窗口的位置判断元素是否滚动到底部,若滚动到底部就加载更多
缺点
scroll
事件不断触发需要做节流操作实现
每次触发事件时都要计算元素的位置性能不好
优点
利用 IntersectionObserver
实现 IntersectionObserver
接口 (从属于Intersection Observer API
) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport
)交叉状态的方法。
优点
IntersectionObserver
是浏览器内部实现的方法,性能较高
可以自定义视窗重叠的阈值
缺点
使用 IntersectionObserver
的一般流程 第一步 初始化 IntersectionObserver
,构建 IntersectionObserver
实例,确定视窗窗口和触发阈值及回调函数
1 const ob = new IntersectionObserver (callback, options)
说明 callback 当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数
entries: 一个 IntersectionObserverEntry 对象的数组,每个被触发的阈值,都或多或少与指定阈值有偏差。
observer: 被调用的 IntersectionObserver 实例。
options(可选) 一个可以用来配置 observer 实例的对象。如果 options 未指定,observer 实例默认使用文档视口作为 root,并且没有 margin,阈值为 0%(意味着即使一像素的改变都会触发回调函数)。你可以指定以下配置:
root: 监听元素的祖先元素 Element 对象,其边界盒将被视作视口。目标在根的可见区域的的任何不可见部分都会被视为不可见。
rootMargin: 一个在计算交叉值时添加至根的边界盒(bounding_box)中的一组偏移量,类型为字符串(string) ,可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和 CSS 中的 margin 属性等同; 可以参考 The root element and root margin in Intersection Observer API 来深入了解 margin 的工作原理及其语法。默认值是”0px 0px 0px 0px”。
threshold: 规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组 0.0 到 1.0 之间的数组。若指定值为 0.0,则意味着监听元素即使与根有 1 像素交叉,此元素也会被视为可见. 若指定值为 1.0,则意味着整个元素都是可见的。阈值的默认值为 0.0。
第二步 调用 observe
观测元素,targetElement
即为观测的元素
1 ob.observe (targetElement)
第三步 调用 unobserve
取消对元素的观测,target
即为取消观测的对象
第四步 调用 disconnect
终止对所有目标元素可见性变化的观察
在 React
中的实现
初始化 IntersectionObserver
,确定视窗窗口和触发阈值及回调函数
通过 children
属性获取子组件中的最后一个元素
监听列表中的最后一个组件,当最后的组件触发事件时加载更多
引入 intersection-observer
组件做兼容性处理
完整代码参考 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 import React from 'react' import 'intersection-observer' interface IProps extends React .DOMAttributes <HTMLDivElement > { children : any getRootElement?: (parent : Element ) => Element rootMargin?: string observerCallback?: IntersectionObserverCallback } class LoadMore extends React.PureComponent <IProps > { private ele : Element | null = null private observer : IntersectionObserver | undefined private observerElement : Element | null = null private lastThreshold : number = 0 public componentDidMount ( ) { const { rootMargin } = this .props this .observer = new IntersectionObserver (this .observerCallback , { root : this .getRootElement (), rootMargin, threshold : [0 , 0.01 ], }) this .listenLastDom () } public componentDidUpdate (prevProps : IProps ) { const { children : oldChildren } = prevProps const { children } = this .props const len = React .Children .toArray (children).length if (len !== React .Children .toArray (oldChildren).length && len) { this .listenLastDom () } } public componentWillUnmount ( ) { this .unobserve () } public render ( ) { const { children, getRootElement, observerCallback, rootMargin, ...otherProps } = this .props return ( <div ref ={this.getElement} {...otherProps }> {children} </div > ) } private getRootElement = () => { const { getRootElement } = this .props const ele = this .ele as Element let parent = null if (getRootElement) { parent = getRootElement ( ele && ele.parentElement ? ele.parentElement : (document .body as Element ), ) } if (!parent && ele && ele.parentElement ) { parent = ele.parentElement } return parent } private getElement = (ele : Element | null ) => { this .ele = ele } private observerCallback = ( entries : IntersectionObserverEntry [], observer : IntersectionObserver , ) => { const [{ intersectionRatio = 0 }] = entries const { observerCallback } = this .props if (intersectionRatio > this .lastThreshold ) { if (observerCallback) { observerCallback (entries, observer) } } this .lastThreshold = intersectionRatio } private unobserve = () => { if (this .observerElement && this .observer ) { this .observer .unobserve (this .observerElement ) } } private listenLastDom = () => { if (this .ele && this .ele .lastChild ) { const dom : Element = this .ele .lastChild as Element this .lastThreshold = 0 const observer = this .observer as IntersectionObserver if (dom && observer) { this .unobserve () this .observerElement = dom observer.observe (this .observerElement ) } } } } export default LoadMore
参考