原文連接: hackernoon.com/naive-infin…javascript
本文爲 RxJS 中文社區 翻譯文章,如需轉載,請註明出處,謝謝合做!css
若是你也想和咱們一塊兒,翻譯更多優質的 RxJS 文章以奉獻給你們,請點擊【這裏】html
這是一次嘗試,使用 RxJS 來實現簡單的無限滾動加載。java
Angular 版本實現的文章: 使用 RxJS Observables 來實現簡易版的無限滾動加載指令node
簡單來講,它使用異步數據流進行編程。這有一篇 Andre Staltz 所寫的超棒文章及 egghead 提供的配套視頻:react
文章: 不容錯過的響應式編程介紹git
視頻: egghead.io/courses/int…es6
RxJS 或 Reactive Extensions 是最早由 Microsoft Open Technologies 開發的用於轉換、組合和查詢數據流的庫。github.com/Reactive-Ex… (譯者注: 這是 V4 版本的 RxJS)github
這是 Ben Lesh 的演講 用響應式的思惟來使用 RxJS 5,很是棒。ajax
下面是 Netanel Basal 對 Observables 和少數操做符的一些精彩介紹:
咱們將要使用 observables 來開發一個簡單的無限滾動加載。當用戶滾動到指定容器高度的70%,咱們就調用 API 從服務器獲取更多的數據。咱們會使用 HackerNews 的非官方 API 來獲取最新的新聞。
下面是咱們將使用到的 RxJS 操做符:
jsbin.com 上的完整示例: output.jsbin.com/punibux
導入 RxJS 庫並使用 infinite-scroller
做爲滾動容器的 id,獲取的全部新聞都將追加到此容器中。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Naive Infinite Scroller - RxJS</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.min.js"></script>
</head>
<body>
<ul id="infinite-scroller">
</ul>
</body>
</html>
複製代碼
#infinite-scroller {
height: 500px;
width: 700px;
border: 1px solid #f5ad7c;
overflow: scroll;
padding: 0;
li {
padding : 10px 5px;
line-height: 1.5;
&:nth-child(odd) {
background : #ffe8d8;
}
&:nth-child(even) {
background : #f5ad7c;
}
}
}
複製代碼
let currentPage = 1;
const getQuotesAPI = () => {
return 'https://node-hnapi.herokuapp.com/news?page=' + currentPage;
};
/** 處理 API 返回的數據 **/
const processData = res => {
res.json()
.then(news => {
currentPage++;
news.forEach(renderNews);
});
};
/** 渲染每條信息 **/
const renderNews = (news) => {
const li = document.createElement('li');
li.innerHTML = `${news.id} - ${news.title}`;
scrollElem.appendChild(li);
};
/** 檢查用戶是否向下滾動,經過前一個滾動位置 和當前滾動位置進行判斷 **/
const isUserScrollingDown = (positions) => {
return positions[0].sT < positions[1].sT;
};
/** 檢查滾動位置是否達到了要求的容器百分比高度 **/
const isScrollExpectedPercent = (position, percent) => {
return ((position.sT + position.cH) / position.sH) > (percent/100);
};
複製代碼
前面三個函數都很簡單:
getQuotesAPI
— 返回 API 的 url,此 url 使用當前頁碼做爲查詢參數。processData
— 處理 fetch API 返回的數據並增長當前頁碼。renderNews
— 接收每條新聞數據並將其渲染到頁面中。後面兩個函數用來進行滾動計算:
isUserScrollingDown
— 檢測用戶是否向下滾動。isScrollExpectedPercent
— 檢測用戶是否已經滾動指定的百分比,從而加載更多數據。/** 設置流 **/
const scrollElem = document.getElementById('infinite-scroller');
const scrollEvent$ = Rx.Observable.fromEvent(scrollElem, 'scroll');
複製代碼
要捕獲容器的滾動事件,咱們須要建立滾動事件的 observable 。使用 Rx.Observable.fromEvent
就能夠完成。在變量結尾處加 $
是一種慣例,以表示引用的是 observable 流。
/** 流的邏輯 **/
const userScrolledDown$ = scrollEvent$
.map(e => ({
sH: e.target.scrollHeight,
sT: e.target.scrollTop,
cH: e.target.clientHeight
}))
.pairwise()
.filter(positions => {
return isUserScrollingDown(positions) && isScrollExpectedPercent(positions[1], 70))
});
const requestOnScroll$ = userScrolledDown$
.startWith([])
.exhaustMap(() => Rx.Observable.fromPromise(fetch(getQuotesAPI())))
/** 訂閱以產生效果 **/
requestOnScroll$.subscribe(processData);
複製代碼
咱們將接收由 scrollEvent
$ 發出的滾動事件並將其映射成無限滾動加載邏輯所須要的值。咱們只取滾動元素的三個屬性: scrollHeight
、 scrollTop
和 clientHeight
.
將映射過的數據傳給 pairwise
操做符,它會發出由當前值和前一個值組成的數組,以下圖所示。
如今咱們將這組位置數據傳給 filter
操做符來根據條件進行過濾:
當 userScrollDown$
產生符合過濾條件的值後會調用 requestOnScroll$
。咱們給了 requestOnScroll$
一個空數組做爲初始值。
咱們使用 Rx.Observable.fromPromise
來將 promise 轉化成 observable 。fetch
發起 http 請求並返回 promise 。exhaustMap
會進行等待,直到 fetch 完成而且內部 observable 發出 API 返回的數據。
Observables 是懶加載的。這意味着除非訂閱了它們,它們纔會執行。咱們訂閱 requestOnScroll$
並傳入 processData
做爲訂閱方法。當 exhaustMap
發出 API 的返回數據後,數據會傳給 processData
,而後執行 renderNews
將數據渲染到頁面中。
下面的 gif 圖片實際演示了無限滾動加載的效果,注意觀察右邊的滾動條。
更新: 這是我下篇文章的連接 使用 RxJS Observables 來實現簡易版的無限滾動加載指令