阅读 168

RxJS示例

注意版本

以下示例基于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)
复制代码

附录

blog.jerry-hong.com/series/rxjs… rxjs.dev/ cn.rx.js.org/

文章分类
前端
文章标签