注意版本
以下示例基于
rxjs@7.3.0官方最新版本 RxJS 版本6之前的语法有较大差异 注意区分
<script src="https://cdn.bootcdn.net/ajax/libs/rxjs/7.3.0/rxjs.umd.js"></script>
核心概念
- Observable 可观察对象
- 代表一组
未来即将产生的事件对象(被观察的方法)
- 代表一组
- Observer 观察者对象
- 代表一个用来接收
【观察结果】对象(收到的就是事件对象) - 观察者对象就是一个对象包含3个含有回调函数的属性(next, error, complete)
- 代表一个用来接收
- Subscription 订阅对象
- 代表正在执行 Observable/Observer 的执行个体(可以用来取消订阅)
- Operators 运算符
- 必须拥有
函数编程中所定义的纯函数特性(没有副作用的函数) - 主要用来处理一系列的
对象方法集合 - 常见的包含 map filter concat flatMap switchMap...
- 必须拥有
Functional Programming
(5 + 6) - 1 * 3
我們把每個運算包成一個個不同的 function,並用這些 function 組合出我們要的結果,這就是最簡單的 Functional Programming。
const add = (a, b) => a + b
const mul = (a, b) => a * b
const sub = (a, b) => a - b
sub(add(5, 6), mul(1, 3))
可读性
[9, 4].concat([8, 7]) // 合并
.sort() // 排序
.filter(x => x > 5)
可维护性高
应为 Pure function 等特性,执行结果不依赖外部,且不会对外部环境有任何操作,使 Functional Programming 能更好的除错以及单元测试。
易于并行/平行处理
ForEach
var arr = ['Jerry', 'Anna'];
for(var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
一 DOM点击
javascript
document.addEventListener('click', (ev) => {
console.log(ev)
})
RxJS
// 取出 fromEvent 方法
const { fromEvent } = rxjs;
// 订阅 click 事件
const clicks$ = fromEvent(document, 'click').subscribe(console.log);
// 取消订阅
clicks$.unsubscribe()
二 限制DOM点击 ev.clientX < 1000
const { fromEvent } = rxjs
const { filter } = rxjs.operators
var clicks$ = fromEvent(document, 'click')
var subs$ = clicks$.pipe(filter(ev => ev.clientX < 1000)).subscribe((x) => console.log(x))
三 限制DOM点击 ev.clientX < 1000 且取前三次
const { fromEvent } = rxjs
const { filter, take } = rxjs.operators
var clicks$ = fromEvent(document, 'click')
var subs$ = clicks$.pipe(
filter(ev => ev.clientX < 1000),
take(3)
).subscribe((x) => console.log(x))
三 限制DOM点击 ev.clientX < 1000 且取前三次
const { fromEvent } = rxjs
const { filter, take } = rxjs.operators
var clicks$ = fromEvent(document, 'click')
var subs$ = clicks$.pipe(
filter(ev => ev.clientX < 1000),
take(3)
).subscribe((x) => console.log(x))
四 限制DOM点击 ev.clientX < 1000 且取前三次 且点击间隔超过800的只取最后一次
const { fromEvent } = rxjs
const { filter, take, debounceTime } = rxjs.operators
var clicks$ = fromEvent(document, 'click')
var subs$ = clicks$.pipe(
debounceTime(800),
filter(ev => ev.clientX < 1000),
take(3)
).subscribe((x) => console.log(x))
定时输出
// 取出 interval 方法
const { interval } = rxjs
// 取出 take 运算符
const { take } = rxjs.operators
// subscribe 订阅
interval(1000).pipe(take(4)).subscribe(console.log)
无限滚动列表
#infinite-scroller {height: 500px;width: 700px;border: 1px solid #f5ad7c;overflow: scroll;padding: 0;}
li {padding : 10px 5px;line-height: 1.5;}
li:nth-child(odd) {background : #ffe8d8;}
li:nth-child(even) {background : #f5ad7c;}
<ul id="infinite-scroller"></ul>
/**
* 前置
* map : 与数组的 map 类似,映射传入的数据流。
* filter : 与数组的 filter 类似,过滤传入的数据流。
* pairwise : 返回由当前发出值和前一个发出值组成的数组。
* startWith : 返回的 observable 会在发出源 observable 的值之前先发出提供的值。
* exhaustMap : 只有当内部 observable 完成后,才会发出新的值。
*/
/**
* 步骤
* 1 getQuotesAPI — 返回 API 的 url,此 url 使用当前页码作为查询参数。
* 2 processData — 处理 fetch API 返回的数据并增加当前页码。
* 3 renderNews — 接收每条新闻数据并将其渲染到页面中。
* 4 isUserScrollingDown — 检测用户是否向下滚动。
* 5 isScrollExpectedPercent — 检测用户是否已经滚动指定的百分比,从而加载更多数据。
*/
const { fromEvent, from } = rxjs;
const { map, filter, pairwise, startWith, exhaustMap } = rxjs.operators
let currentPage = 1
const getQuotesAPI = () => {
return 'https://node-hnapi.herokuapp.com/news?page=' + currentPage;
}
const processData = res => {
console.log(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 = (position) => {
return position[0].sT < position[1].sT
}
const isScrollExpectedPercent = (position, percent) => {
return ((position.sT + position.cH) / position.sH) > (percent / 100)
}
// 设置 observable 流
const scrollElem = document.getElementById('infinite-scroller');
const scrollEvent$ = fromEvent(scrollElem, 'scroll');
console.log('scrollEvent$', scrollEvent$);
// 编写流的逻辑,负责处理滚动事件和调用 API
const userScrolledDown$ = scrollEvent$.pipe(
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$.pipe(
startWith([]),
exhaustMap(() => from(fetch(getQuotesAPI())))
)
requestOnScroll$.subscribe(processData)