現現在前端React生態圈已有很多優秀的懶加載組件,好比react-lazy-load
、react-lazy-mount
等,可是它們並無實現一個組件徹底的懶加載,當DOM沒有出如今視口中時,它們會先渲染出一部分,等到出如今視口中才會渲染要懶加載的目標,大多數基本都是圖片懶加載,雖然已經知足了絕大多數要求,可是過程並非很完美。而真正實現組件懶加載的開源庫,仍是經過scroll
事件監聽出如今窗口中的DOM,性能並非很優秀,筆者所開源的這個組件既不用使用scroll
事件,也能實現組件懶加載。javascript
這個組件所依賴的API是IntersectionObserver
,關於這個API掘金已有很多文章來描述講解了,我這裏再簡單描述一下:監聽一個DOM元素,如果這個DOM元素出如今你規定的視口中(通常都爲window窗口),調用指定的回調函數
。可是我已看到的全部文章只是使用原生JS的方法來說解怎麼使用。
好比:html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<script> const callback = (entries) => { entries.forEach((item) => { const isIntersecting = item.isIntersecting; if (isIntersecting) { const image = new Image(); image.src = "http://xxxx"; item.target.appendChild(image); } }); }; const io = new IntersectionObserver(callback); const container = document.querySelector(".container"); [...container.children].forEach((ele) => { ele.observe(ele); }); </script>
</body>
</html>
複製代碼
上面代碼是我大概寫的一個使用教程,中只要container
中一個子元素出如今視口中,就加載出一張圖片。可是這種寫法以及思路用在React
或者Vue
中使用是不符合規範的。那麼咱們該如何在React中使用呢?前端
衆所周知,React中是虛擬DOM,也就是一個對象,React內部用這個虛擬DOM渲染成真實的DOM,咱們在React組件內部如何不是操做DOM的話,通常是不會用到真實DOM的,而IntersectionObserver
必須監聽真實的DOM才能夠,那怎麼辦呢?
這時候Ref
就出場了,Ref
掛到一個ReactDOM上能夠幫助咱們拿到當前DOM的全部信息,包括真實DOM。java
const Example = () => {
const domRef = useRef();
return(
<div ref={domRef}> { list.map(item=>(<div>{item}</div>)) } </div>
)
}
複製代碼
利用這個特性咱們就能夠拿到div
的全部信息,包括它的子元素,這樣咱們就能夠經過IntersectionObserver
來監聽它全部的子元素是否出如今視口中。 那麼問題來了,監聽的元素都是已經被渲染出來的元素,那麼還怎麼懶加載呢?接下來這個思路就是step by step
。react
咱們先來看兩個組件:git
const LazyList = (props) => {
const [renderCont,setRenderCount] = useState(2)
const children = props.children;
const renderList = useMemo(()=>{
return children.slice(0,renderCont)
},[children,renderCont])
useEffect(()=>{
//...
},[renderCont])
return(
<div>{renderList}</div>
)
}
const Example = () => {
return(
<LazyList> {list.map(item=>{ <div>111</div> })} </LazyList>
)
}
複製代碼
在LazyList
這個組件中,渲染children
中的renderCont
個,renderList
一變化,在useEffect中的回調函數中經過ref
拿到真實DOM,進行監聽。github
上邊咱們可以先渲染renderCont
個DOM,可是如何進行懶加載呢?就是當renderCont
中最後一個出如今視圖中時,渲染第renderCont + 1
個DOM,setRenderCount(renderCont + 1)
,這裏是在IntersectionObserver
回調函數中進行。循環往復,就實現了組件懶加載。web
注意看控制檯的DOM區域,初始渲染出兩個,每次下拉渲染出來的兩個出線在試圖中,後續兩個DOM就渲染出來了,視覺效果上也不會出現中斷的感受。npm
圖示是我我的項目用來演示的,這個組件剛剛開源,目前我司咱們部門3個項目已經在生產環境使用了,沒有任何問題。而且功能只爲實現長列表懶加載,對於須要一次性渲染大量數據可是不須要徹底展現的業務比較契合,還未暴露接口指示渲染到第幾個DOM,因此有一個功能並不支持:DOM渲染的回調函數,也就是暴露出渲染到了第幾個DOM
,目前正在完善這個功能,下個版本會上線。
此組件在web端和移動端都支持,而且引入了IntersectionObserverpolyfill
,包的整體積爲4.5KB
。 具體代碼以及邏輯看github吧~
github地址:點擊這裏
安裝:markdown
npm i lazylist-react
// or
yarn add lazylist-react
複製代碼
任何疑問均可以在githubissus
留言或者在文章底部評論,我都會看而且回覆。
但願你們多多支持呀😃