布莱恩F爱
从专注于角图,Web技术以yobet英雄联盟及波特兰的Node.js的Google开发人员专家中学习。
广告 ·ultimatecourses.com.
用终极课程学习角度正确的方式

RxJS:掌握操作符

使用Angular 5和NGRX学习掌握RXJS运算符。

Meetup演讲

这篇博客文章主要基于2018年3月的岩石山角度举行展示。由于Jon Rista有助于演示,这是一个很大的感谢!

示例应用程序

在这篇文章中,我将参考Heroes应用程序的样本NGRX游览,特别是* NGRX-Refactor-2 **分支。

你也可以克隆它,然后检出ngrx-重构-2分支:

美元git克隆git@github.com:蓝色/ ngrx-tour-of-heros.git美元git结帐Ngrx-Refactor-2

运营商是什么?

当我引用RxJS的操作符时,我引用的是<代码>可观测的类。它们是纯功能,可在可观察流中转换信息,这些流通常基于当前可观察到的。最重要的是,RXJS中的运算符允许复杂的异步代码,这些代码可以轻松地以声明方式组成。

操作符可以分为多个类别。有创建操作符,可以从一个源(如promise或value)创建一个新的可观察对象,转换操作符将转换流中的数据,过滤操作符将作为可观察流的门。

它们是如何组合的?

所有操作符返回<代码>可观测的,使它们可链化(或可管道化,但不能出租)。这使我们能够使用同步管道中的一系列操作符来组合复杂的逻辑。最后,当我们订阅输出可观察对象时,它也会订阅输入可观察对象。

版本?

在撰写本文时,Angular应用程序中使用了两个常见的RxJS版本。yobet外围5.4版。x在Angular 2和Angular 4中使用,而在version 5.5中使用。RxJS中的x正在Angular 5中使用。还有一个测试版的RxJS 6,你可以看看。

我如何进口?

当你可以导入所有运营商,没有建议,您的用户不会幸福。当您只使用少数时,不需要将所有75多个运算符(及其符号变体)运送给您的用户。

如果您使用的是使用rxjs v5.4.x,那么您将想要使用基于原型的导入导入:

进口“rxjs /添加/运营商/ switchMap”;

这将附加<代码>switchMap方法<代码>可观察到的.Prototype.。

如果您使用的是RXJS v5.5.x,那么您将使用导出函数的ES6样式导入,例如:

进口{switchMap}“rxjs /运算符”;

我如何链式运营商?

同样,答案根据您使用的rxjs的版本而异。rxjs 5.5.x推出了使用leattable运算符语法使用<代码>可观测的。管()方法。

首先,让我们看一个RxJS 5中的链接操作符的例子。x(和Angular 2/4):

邮政编码{私人用户:可观测的<用户>;ngoninit.(){帖子=用户地图(用户= >用户ID)switchMap(ID= >postsServicegetposts.(ID));}}

正如您在上面的例子中看到的,我们使用点表示法将运算符链接在一起。

这里是RXJS 5.5.x(和Angular 5)中的枪支运算符的示例:

邮政编码{私人用户:可观测的<用户>;ngoninit.(){帖子=用户(地图(用户= >用户ID),switchMap(ID= >postsServicegetposts.(ID)));}}

当使用RxJS 5.5时。我们用<代码>管()方法,以相同的顺序将所有操作符传递给该方法。

何角呢?

我以为你永远不会问。如果你是观察者设计模式、响应式编程和强大的RxJS的粉丝,那么作为一名Angular开发者,你很幸运。

Angular RxJS

Angular中的所有异步事件都使用了可观察对象,它们目前是通过RxJS库实现的。最后,Angular也给了我们强大的功能<代码>异步在组件模板中方便地订阅可观察流。它使用起来非常简单和强大。

许多运营商如何?

如果你浏览了RxJS<代码>可观测的文档您可能首先注意到:哇,有很多操作符!

是的,有很多。我想说,截至撰写本文时,在RxJS中有大约75个运算符,这还不包括签名的变体。

如何选择?

好问题。我从开发人员那里听到了很多。

首先,查看位于底部的“找到合适的操作员”向导RxJS主页。很多人都忽略了这一点,而这在学习操作时是非常有用的。

其次,学会看大理石图;因为这将帮助您理解您正在考虑的操作符的行为。说到这,让我们快速回顾一下大理石图。

这些图表是什么?

我们用来表示可观察流的彩色图被称为“大理石图”。

由可观察对象实现的观测器设计模式

在确定应用程序中应该使用的一个或多个操作符时,我们可以参考大理石图。

地图()

的<代码>地图()操作符对于JavaScript开发人员来说是一个很容易的起点,因为它的行为很像<代码>数组。prototype.array ()方法:

getCharacters(的名字:字符串):可观测的<数组<字符>>{如果(的名字长度= = =0){返回可观测的((]);}返回istribleService.getCharacters(的名字)(地图(marvelResponse= >marvelResponse数据结果));}

在上面的例子中<代码>得到characters()方法使用<代码>istribleService.实例检索匹配的字符<代码>的名字指定。本例使用Marvel API根据用户输入的名称获取一个Marvel字符数组。的<代码>得到characters()方法返回一个<代码>可观测的。我们使用<代码>地图()运营商返回<代码>结果属性,它是Marvel API返回的响应对象的一部分。该操作符使我们能够将可观察流的响应映射到另一个值,在本例中是字符结果数组。

过滤()

的<代码>过滤()操作符的行为很像<代码>数组。prototype.filter ()方法:

过滤(的名字:字符串):可观测的<数组<字符>>{如果(的名字长度= = =0){返回可观测的((]);}返回istribleService.getCharacters(的名字)(过滤(marvelResponse= >marvelResponse代码= = =200),地图(marvelResponse= >marvelResponse数据结果));}

在上面的例子中,我们使用<代码>过滤()操作符,只在HTTP响应的状态码为200时向可观察流的观察者发出通知。

点击()或<代码>做()

的<代码>做()操作符被重命名为<代码>点击()在RxJS v5.5。x作为让表操作符升级的一部分,以避免与保留字冲突<代码>做(do-while循环的一部分)。

的<代码>点击()操作符用于执行副作用。操作符接收observable的通知,所以我们可以使用通知的值来执行一个副作用;例如,发送一个操作来更改应用程序的状态。并且,操作符总是返回它接收到的通知。换句话说,它不会转换或过滤通知(或者如果您愿意,也可以使用<代码>这值)。

让我们来看看一个例子:

出口EditComponent实现了oninit.{功率:可观测的<权力>;构造函数(私人activatedRoute.:activatedRoute.,私人小吃店:MatSnackBar,私人商店:商店<powersstate.>){}ngoninit.(){功率=activatedRoute.paramMap(利用(paramMap= >{常量ID=+paramMap得到('ID');商店调度(SelectPower({ID:ID}))haspowerinstore.(ID)订阅(存在= >{如果(!存在){商店调度(LoadPower.({ID:ID}));}});}),switchMap(()= >商店(选择(getSelectedPower))));}}

这有点漫长,所以让我们打破它。

  • 的<代码>EditComponent是一种常规组件,它用于编辑超级电源(或真的,只是一个<代码>权力在MongoDB中的文件)。
  • 的<代码>功率房地产是一个<代码>可观测的的<代码>权力对象。
  • 的<代码>点击()运营商收到了<代码>paramMap,这是一个<代码>地图,价值来自<代码>activatedRoute.。ParAmmap.可观察到的。
  • 与之<代码>ID价值,我们派遣了<代码>SelectPower行动,当商店不包含所选电源时,<代码>LoadPower.。行动。

我们可以用<代码>点击()操作员执行诸如日志记录或副作用之类的任务,例如向商店调度操作。

SwitchMap()

的<代码>SwitchMap()操作符从一个流切换到另一个流,取消订阅前一个observable,并返回一个新的observable。

在前面的例子中,我们使用了<代码>SwitchMap()操作员返回新的可观察到的结果<代码>getSelectedPower选择器,它返回所选的可观察者<代码>权力对象。

另一个常见用例<代码>SwitchMap()在NGRX效果中:

@效果()loadPower:可观测的<行动>=行动减低<LoadPower.>(LOAD_POWER)(地图(行动= >行动有效载荷),switchMap(有效载荷= >powersServicegetPower(有效载荷ID)(重试(3.))),地图(功率= >loadpowersuccess.(功率)),CARTERROR.((e:httperrorresponse.)= >可观测的(httperror.(e))));

在上面的例子中<代码>SwitchMap()运营商收到了<代码>有效载荷来自的通知<代码>LoadPower.。行动并返回一个新的可观察者,结果<代码>getPower()方法中的方法<代码>powersService,它使用<代码>HttpClient从REST API获得强大的功能。

CatchError()或<代码>抓住()

的<代码>抓住()操作符在RxJS v5.5中被重命名。x<代码>CatchError()。喜欢这个名字,我们使用<代码>CatchError()操作员接收在可观察流中发出的任何错误通知。

在前面的例子中,我们使用了<代码>CatchError()操作员捕获任何<代码>httperrorresponse.对象。该示例是ngrx效果,所以我们返回一个新的<代码>httperror.作为错误的结果将被派遣的行动。

第一()

的<代码>第一()操作符返回第一个被观察到的通知,并完成可观察流。我们可以指定<代码>谓词函数过滤特定值。我们还可以指定一个<代码>选择器函数转换操作符返回的值。它支持a<代码>默认的价值。

当我们看着这个时<代码>EditComponent早些时候,我们引用了<代码>haspowerinstore()方法,该方法返回一个<代码>可观测的的<代码>布尔值;要么<代码>真正的当能量存在于仓库时,或者<代码>假当电源在存储中不存在时。这是一种方便的方法来检查一个值是否存在于NgRx存储中:

haspowerinstore.(ID:):可观测的<布尔>{返回商店选择(刺子)(第一(权力= >权力!==,权力= >权力(ID]!==未定义));}

在上面的例子中,我使用<代码>第一()运营商与<代码>谓词和<代码>选择器指定的功能。首先,<代码>谓词要求实体词典不是<代码>零。然后,<代码>选择器函数返回<代码>布尔确定实体是否存在于商店中的值。

最后一个()

相反的<代码>第一()运营商,<代码>最后一个()返回最后一个被观察到的值,并完成可观察流。另一个区别是<代码>最后一个()运算符等待完成返回之前来自原始流的通知。这对于一次性完成的操作非常有用:

withLatestFrom ()

的<代码>withLatestFrom()操作员合并数据流,提供价值另一个可观察到的最新值一旦观察到发出通知(或价值),可观察到。

让我们来看看一个例子:

ngoninit.(){英雄=功率(withLatestFrom(heroesService柠檬枥()),地图(((功率,英雄])= >英雄过滤(英雄= >英雄权力indexOf(功率ID)>-1)));}

在这个例子中,我们正在唤醒<代码>英雄有选择的<代码>功率。的<代码>withLatestFrom()操作符合并的结果<代码>柠檬枥()可观察流<代码>功率流。的<代码>地图()操作符以数组的形式接收observable流发出的两个值。我们使用解构来访问数组中的第一个和第二个值,即<代码>功率对象和数组<代码>英雄。

forjoin()

的<代码>forkjoin()运算符类似于<代码>Promise.all ()方法,因为它会同时启动(分叉)多个观察者,然后在所有可观察对象完成后连接每个可观察对象的最终值。重要的是要注意,如果任何一个输入的observable都没有完成,那么<代码>forkjoin()永远不会完成。

让我们来看看一个例子:

英雄:可观测的<英雄>权力:可观测的<数组<权力>>;ngoninit.(){英雄=activatedRoute.paramMap(常量ID=+paramMap得到('ID');switchMap(paramMap= >heroesServicegetHero(ID));权力=英雄(mergeMap(英雄= >forkjoin.(英雄权力地图(ID= >powersServicegetPower(ID)))));}
  • 在上面的例子中,我们在一个组件中设置了两个属性:<代码>英雄和<代码>权力。
  • 的<代码>英雄房地产是一个<代码>可观测的的<代码>英雄对象,和<代码>权力是一个<代码>可观测的一系列<代码>权力对象。
  • 的<代码>权力属性是加载与所选英雄相关的每个能力的结果。
  • 的<代码>英雄对象有<代码>权力的数组<代码>ID给英雄的每一种能力值。
  • 的<代码>forkjoin()函数返回的所有可观察对象的分支<代码>getPower()方法,然后等待所有的可观察对象完成,然后返回对应可观察对象的最后一个值的数组。

mergeMap ()

的<代码>mergeMap()操作符将源observable接收到的值映射到一个返回observable的函数中,并合并该observable发出的值。

在上面的例子中,我们使用了<代码>mergeMap()接受<代码>英雄由源发出的值<代码>this.hero可观察到的。然后我们使用<代码>forkjoin()操作符返回数组的单个observable<代码>权力对象返回的<代码>getPower()方法。然后使用<代码>mergeAll ()。

DistintuntuntilChanged()

的<代码>DistintuntuntilChanged()操作符只会从源可观察对象中发出不同的值。默认情况下,它使用严格的等式检查,或者您可以指定<代码>比较器函数来确定值的唯一性。

让我们来看看一个例子:

ngoninit.(){形成valueChanges(debounceTime(500.),distintuntuntilchanged((上一页:权力,下一个:权力)= >上一页的名字= = =下一个的名字))订阅(价值= >{如果(!形成有效){返回;}PowerChange.发出({功率,价值});});}

在这个例子中,我们只想发出<代码>权力Change.事件时的名称<代码>权力对象已更改为表单。的<代码>DistintuntuntilChanged()允许我们过滤可观察流,并只在我们确定值在可观察流中发生了变化时才发出该值。这有助于我们避免保存相同的数据,比如,用户与表单值交互,但随后又将其更改回原始值。

debounceTime ()

在前面的示例中,我们还使用了<代码>debounceTime()操作符等待,直到从可观察流发出的最后一个值已经过去500毫秒。使用<代码>debounceTime()操作员我们可以避免发出<代码>权力Change.输入输入的每个键的事件。如果在间隔过去之前到达新值,则丢弃发出的先前值。

结论

首先,使用RXJS库的无功编程是用于与可观察到的异步事件或数据流一起使用的范例。使用运算符过滤,转换或更改数据流的定时使我们能够轻松地将逻辑与可观察流进行逻辑。

其次,如果你是Angular开发者,对RxJS中的基本操作符有一个很好的理解,将大大提高你创建应用的能力,这些应用会消耗和创建异步事件和值。在用Angular构建应用时,几乎不可能不使用RxJS和observable。

最后,学习阅读和理解大理石图对理解各种运营商非常有益。

布莱恩F爱

嗨,我是布莱恩。我对类型名称,Angular和node.js感兴趣我嫁给了我最好的朋友邦妮,我住在波特兰和我滑雪(很多)。