响应式编程就是一种异步数据流和变化传播的编程规范,可以简单的理解为是观察者模式与Rxj的开发模式,在流的操作中通过一些特定的操作符,从旧的流转换为新的流,然后监听变化
一、rxjs
的基本使用及基本术语
安装使用
- 1、传统方式
<script src="http://cdn.bootcss.com/rxjs/5.4.0/Rx.js"></script>
2、
npm
安装包的使用import {Observable} from 'rxjs/Observable' // 1. 按需打包,减轻bundle.js大小 import 'rxjs/add/observable/merge'; // 2. 按需导入函数,如merge
- 1、传统方式
一些专业术语
Observable
( 可被观察的 ) : 是一个包含来自未来、可以被使用的值(value
)或事件(event
)的集合Observe
( 观察者 ):是一个知道如何监听、处理来自Obervable
的值的函数集合Subscription
( 订阅 ):代表着Observable
的执行动作,我们可以使用它来停止Obervable
继续执行Operators
( 操作 ):一系列可以操作集合的pure function
,像是过滤(filter
)、转换(map
)等等Subject( )
:相当于一个事件发射器,是唯一能够向多个Observer
广播值(value
)的唯一手段Schedulers
( 调度 ):是一个中央调度员,帮助我们控制并发,协调计算(setTimeout、requestAnimationFrame
等 )
Observable
(被观察者)四大核心:创建 、订阅 、 执行 、销毁- 前面说的
RXJS
可以简单理解为javascript
里面的观察者与被观察者模式模式 - 那么
RXJS
一样的具有创建一个被观察对象,订阅这个观察对象,执行与取消订阅的功能 subscribe
为观察者订阅一个被观察者unsubscribe
取消订阅
- 前面说的
Subject
是一个特殊的Observable
可以当一个value
成为观察者
//下面是angular中的一个输入框的
public searchText:Subject<string> = new Subject<string>();
二、常见的转换为Rx
流的方式
1、
fromEvent(dom元素,事件名称)
将网页中的DOM
事件转换为流式编程<input type="button" value="按钮" class="btn" /> <input type="button" value="按钮" class="btn" /> <input type="button" value="按钮" class="btn" /> <input type="button" value="按钮" class="btn" /> <input type="button" value="按钮" class="btn" />
var btn = document.querySelectorAll(".btn"); Rx.Observable.fromEvent(btn, 'click') .subscribe(count => console.log('你点击了我'));
2、
from(数组)
将数组转换为流式编程Rx.Observable.from([1, 2, 3, 4, 5, 6, 7, 8, 9]) .filter(result => result % 2 == 0) .subscribe(rs => console.log(rs));
3、
of(元素1,元素2,元素2)
将一组数据转换为流式编程Rx.Observable.of(1, 2, 3, 4, 5, 6, 7, 8, 9) .map(re => re * 2) .subscribe(result => console.log(result));
4、
interval
表示每隔多少时间发送一个流Rx.Observable.interval(1000) .map(re => { console.log("我是值", re); return re; }) .subscribe(x => console.log(x));
三、常见的操作符
1、
map
将一个旧的流转换为一个新的流Rx.Observable.of(1,2,3,4,5,6,7,8,9) .map(e => { console.log(e); return e; }) .subscribe(re => console.info(re));
处理事件操作的时候
Rx.Observable.fromEvent(btn, "click") .map(ev => ev.target.value) .subscribe(re => console.log(re));
2、
filter
对一个旧的流进行过滤成一个新的流Rx.Observable.from([1, 2, 3, 4, 5, 6, 7, 8, 9]) .filter(result => result % 2 == 0) .subscribe(rs => console.log(rs));
3、
first
表示流中的第一个元素Rx.Observable.from(["张三", "李四", "王五"]) .first() .subscribe(x => console.log(x));
4、
last
表示流中的最后一个元素Rx.Observable.from(["张三", "李四", "王五"]) .last() .subscribe(x => console.log(x));
5、
elementAt
获取指定下标的值Rx.Observable.from(["张三", "李四"]) .elementAt(0) .subscribe(x => console.log(x));
6、
scan
操作符和数组中reduce
方法的类似, 它需要一个传递给回调函数的参数值。
回调函数的返回值将成为下一次回调函数运行时要传递的下一个参数值Rx.Observable.fromEvent(btn, 'click') .scan(count => count + 1, 0) .subscribe(count => console.log(`Clicked ${count} times`));
7、
throttleTime
间隔多少时间再执行传统的
javascript
的写法var count = 0; var rate = 1000; var lastClick = Date.now() - rate; var button = document.querySelector('button'); button.addEventListener('click', () => { if (Date.now() - lastClick >= rate) { console.log(`Clicked ${++count} times`); lastClick = Date.now(); } });
使用
Rxjs
的写法Rx.Observable.fromEvent(btn, "click") .throttleTime(1000) .scan(count => count + 1, 0) .subscribe(count => console.log(`Clicked ${count} times`));
8、
debounceTime
在一定时间内只取最新数据进行发射,其他数据取消发射(常用输入框)<input type="text" class="input" /> <input type="button" value="按钮" class="btn1" />
var input = document.querySelector(".input"); Rx.Observable.fromEvent(input, "keyup") .debounceTime(500) .map(ev => ev.target.value) .subscribe(re => console.log(re));
9、
delay
延迟和debounceTime
类似的功能Rx.Observable.fromEvent(btn, "click") .delay(1000) .scan(count => count + 1, 0) .subscribe(count => console.log(`Clicked ${count} times`));
10、
pluck
是从一组数据里面提取想要的(将上面的代码改版下)Rx.Observable.fromEvent(input, "keyup") .pluck("target", "value") .subscribe(re => console.log(re));
11、
debounce
防抖动,只有当另一个Observable发射值时,才取源Obervable的最新数据进行发射,其他数据取消发射。Rx.Observable.interval( 1000 ) .debounce(( ) => Rx.Observable.fromEvent(document, 'click')) .subscribe(x => console.log( x ));
12、
distinct
去除重复的(类似数组去重复)Rx.Observable.from([1, 2, 3, 2, 1, 4, 5, 3, "2", "3"]) .distinct() .subscribe(result => console.log(result));
13、
distinctUntilChanged
去除连续重复的Rx.Observable.from(['A', 'B', 'B', 'A', 'B', 'A', 'A']) .distinctUntilChanged() .subscribe(result => console.log(result));
14、
distinctKeyUntilChanged
去除连续中具有相同的key
的(用于数组对象中)let items = [ {age: 4,name: 'Foo'}, {age: 7,name: 'Bar'}, {age: 5,name: 'Foo'}, {age: 6,name: 'Foo'} ]; Rx.Observable.of(...items) .distinctUntilKeyChanged('name') //去除具有连续相同nam的值 .subscribe(x => console.log(x))
15、
sampleTime
每隔多少时间发送一个数据类似setInterval()
Rx.Observable.interval(1000) //之前介绍过interval()是隔多少时间发射一个Observable .sampleTime(2000) //sampleTime()表示每隔多少时间发射一个流出来 .subscribe(x => console.log(x));
16、获取事件信息
Rx.Observable.fromEvent(document, "click") .map(ev => { console.log(ev); return ev.clientX; }) .subscribe(re => console.log(re));
17、
combineLatest
合并两个流,前提是必须两个都有流,不然是不会发射的<input type="text" id="weight" />kg <br/> <input type="text" id="height" />cm <br/> <div> 使用combineLatest计算结果:<span id="result"></span> </div>
let weight = document.getElementById("weight"); let height = document.getElementById("height"); let result = document.getElementById("result"); let weight$ = Rx.Observable.fromEvent(weight, "keyup") .pluck("target", "value"); let height$ = Rx.Observable.fromEvent(height, "keyup") .pluck("target", "value"); let result$ = Rx.Observable.combineLatest(weight$, height$, (w, h) => w / (h * h / 100) / 100); result$.subscribe(re => result.innerHTML = re);
18、
concat
链式拼接两个Observable
的结果Rx.Observable.of( 1, 2, 3 ) .concat( 'a', 'b', 'c' ) .subscribe( x => console.log( x ));
19、
zip
组合多个Observable
,并生成一个新的Observable
,其值能够通过每个Observable
的值,和自定义函数进行定义。let age$ = Rx.Observable.of<number>(27, 25, 29); let name$ = Rx.Observable.of<string>('Foo', 'Bar', 'Beer'); let isDev$ = Rx.Observable.of<boolean>(true, true, false); Rx.Observable .zip(age$, name$, isDev$, (age: number, name: string, isDev: boolean) => ({ age, name, isDev })) .subscribe(x => console.log(x)); // 输出 // {age: 27. name: 'Foo', isDev: true } // {age: 25. name: 'Bar', isDev: true } // {age: 29. name: 'Bear', isDev: false}
- 20、关于更多的操作符的使用请参考
四、关于subscribe
的补充说明
subscribe
可以定义三个方法,第一个是输出结果,第二个是错误的,第三个最后都要输出的
五、将普通函数改成Observable
的方式
普通函数
function foo() { console.log("hello"); return 42; } var x = foo.call(); console.log(x);
Observable
的方式- 1、
next
可以多个 - 2、
next
类似传统函数的return
类似ES6
中的Generator
的yield
- 3、使用
Observable
可以传递多个值
var observable = Rx.Observable.create(function(observer) { console.log("hello"); observer.next(142); observer.next(42); }); observable.subscribe(re => console.log(re)); observable.unsubscribe(); //取消订阅
- 1、