记得刚学JavaScript的时候,我是从原生的domapi入手,用原生的domapi完成一些增删改查。之后,我将学习JQuery。刚开始接触JQuery的时候,觉得这太酷了。比如这样一段逻辑:创建一个包含文本节点的p标签,然后插入到容器中。用原生domapi写是这样的:constcontainerDom=document.getElementById('container');consttextDom=document.createTextNode("HelloWorld.");constparaDom=document.createElement("p");pparaDom.appendChild(textDom);containerDom.appendChild(paraDom);并像这样用JQuery编写:const$container=$('#container');$container.append('
HelloWorld.
');比起用原生的domapi写,简单多了!这就是JQuery在当时如此流行的原因。虽然现在用Vue、React等数据驱动的前端框架来写页面,但基本不会直接操作dom。但是,当涉及到一些活动页面等需要直接操作dom的场景时,使用JQuery还是很方便的。那么JQuery做了什么?jQuery封装了DOM层,提供了很多操作DOM的API,并且支持链式调用,可以很方便的组织DOM操作逻辑,也支持插件自定义链式调用使用中的一些方法。也许你会说,JQuery不是基本没用吗,何必提呢?因为我觉得JQuery把dom操作封装的很好,这样操作dom的复杂度就降低了很多。前端除了经常操作dom之外,还经常处理异步,比如XHR、Fetch、EventListener等,虽然可以用Promise进行封装,可以进一步简化为async/await,但是Promise和async/await只改变异步逻辑的编写形式,并没有降低编写异步逻辑的复杂度。能不能像JQuery封装dom操作一样封装异步逻辑,简化异步逻辑的写法呢?确实有这样一个库,就是Rx.js。在写JQuery的时候,我们封装了一层dom,比如const$container=$(dom),这样我们就可以使用JQuery自带的工具函数或者通过插件扩展的一些函数,通过链式调用把逻辑串起来。为了方便和dom对象区别,我们将JQuery对象命名为$、$yy等。那么Rx.js第一步就是包裹一层异步逻辑:也就是包裹一层异步代码这样作为EventListener、Promise和回调函数://包裹一层EventListenerconstobservable$=Rx.Observable。fromEvent(document.querySelector('按钮'),'点击');//包裹一层Promiseconstobservable2$=Rx.Observable.fromPromise(fetch('/users'));//包裹一层回调constobservable3$=Rx.Observable.bindCallback(fs.exists);打包后的对象不叫RxJS对象,而是Observable对象,为了区别起见,我们将其命名为xxx$、$,就像JQuery对象,我们将其命名为$、$yyy一样。然后你可以使用一系列内置的工具函数,它们被称为运算符:observable$.pipe(throttleTime(300),take(3),map(event=>event.target.value));比如异步逻辑,我们如果经常做节流处理,不需要自己写,直接使用内置的算子throttleTime即可。同样忽略前三个事件take(3),将数据映射一次(map(()=>xxx)等。这些常见的异步逻辑用操作符写起来非常简单。将异步逻辑组织成链(或管道),使用operators来编写每一步的处理逻辑,然后串联起来,从而将异步逻辑的编写变成了管道的组织。而且就像JQuery可以通过写插件来扩展一样,Rxjs也支持自定义operators。通过后通过这个pipeline,数据经过了每一步异步逻辑的处理,我们可以通过subscribe监听得到最终的数据observerable$.subscribe((value)=>{//xxx})当然也有可能处理过程中出现错误,所以应该把错误传递下去,最后处理完会有通知,所以可以写这三种情况的处理:observable$.subscribe({next:(v)=>{},error:(err)=>{},complete:()=>{}});这些处理逻辑称为观察者。这就是RxJs所做的。因为异步逻辑是对事件的响应,所以这也被称为反应式编程。我们刚刚创建的Observable是一层EventListener、callback和Promise。当然,我们也可以直接创建这样一个Observable对象:比如我们将一串数字封装到Observable中://multipledataconstobservable$=Rx.Observable.of(1,2,3);//数组中的多个数据constobservable2$=Rx.Observable.from([1,2,3]);或者经过一些逻辑逻辑生成一系列数据:varobservable$=newRx.Observable(function(observer){observer.next(1);observer.next(2);observer.next(3);setTimeout(()=>{observer.next(4);observer.complete();},1000);});或者这个:constobservable$=newRx.Subject();可观察的$.next(1);可观察的$.next(2);这里的区别在于Subject可以在外部调用next生成数据,而newObservable在回调函数中调用next生成数据。总结一下:就像JQuery包裹了一层dom,然后提供了简化dom操作的api一样,RxJS也包裹了一层异步逻辑,然后提供了一系列的操作符。我们可以将EventListenr、Promise、callback等封装成Observable(或者用of、from、Subject等创建Observable),然后使用内置或者扩展的oprator来组织处理管道,最后使用Observer接受数据和处理错误的管道。这样,异步逻辑的编写就转化成了算子流水线的组织。当你对内置的算子足够熟练或者自己积累了一些算子后,写异步逻辑的速度就会变得非常快。因为RxJS只是对异步逻辑的封装,与Vue、React等前端框架不冲突,可以很好的结合。(Angular甚至默认集成了RxJS)比如在Vue中,我们可以将事件封装成一个带有Subject的Observable,然后使用RxJS的算子来组织异步逻辑:点我??
import{Subject}from'rxjs'import{debounceTime}from'rxjs/operators'exportdefault{data(){return{observable$:newSubject()}},created(){this.observable$.pipe(debounceTime(500)).subscribe((event)=>{//xxx})},methods:{clickHandler(event){this.observable$.next(event)}}}在React中是一样的,使用Subject自己创建了一个Observale,可以将异步逻辑的编写转化为运算符的组装:classMyButtonextendsReact.Component{this.state={count:0};this.observable$=newRx.Subject();this.observable$.pipe(debounceTime(500),map(()=>1),scan((total,num)=>total+num,0));this.observable$.subscribe(x=>{this.setState({count:x})})}render(){return