什么是rxJS中的管道

enn*_*oid 66 rxjs rxjs5 angular

我认为我有基本概念,但有一些晦涩难懂

所以一般来说这就是我使用observable的方式:

observable.subscribe(x => {

})
Run Code Online (Sandbox Code Playgroud)

如果我想过滤数据,我可以使用:

import { first, last, map, reduce, find, skipWhile } from 'rxjs/operators';
observable.pipe(
    map(x => {return x}),
    first()
    ).subscribe(x => {

})
Run Code Online (Sandbox Code Playgroud)

我也可以这样做:

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/first';

observable.map(x => {return x}).first().subscribe(x => {

})
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:

  1. 有什么不同?
  2. 如果没有区别,为什么功能管存在?
  3. 为何这些功能需要不同的进口?

mar*_*tin 54

"pipable"(以前的"lettable")运算符是自RxJS 5.5以来使用运算符的当前和推荐方式.

我强烈建议您阅读官方文档https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md

主要区别在于,Observable如果两个不同方想要创建同名的运算符,那么更容易制作自定义运算符并且更好的树可用而不改变某些可能发生冲突的全局对象.

import为每个运算符使用单独的语句'rxjs/add/operator/first'是一种制作较小的应用程序包的方法.通过仅导入您需要的运算符而不是整个RxJS库,您可以显着减少总包大小.但是,编译器无法知道您是否导入,'rxjs/add/operator/first'因为您在代码中确实需要它,或者在重构代码时忘记删除它.这是使用pipable运算符的优势之一,其中自动忽略未使用的导入.

  • @AdamFaryna 当然,有些团队也可能在纸上编写代码,但是如果他们有可用的现代工具,他们为什么要这样做呢?使用文本编辑器,尤其是没有重要插件的情况下,类似于在纸上编写代码。你可以这样做,但为什么任何体面的团队/开发人员会这样做 (3认同)
  • 关于您的断言“未使用的导入将被自动忽略”,目前 IDE 具有删除未使用的导入的插件。 (2认同)
  • 并不是每个人都在使用这些 IDE 或这些插件,很多人都使用基本的文本编辑器。可能大多数时候我们不能相信团队中的每个人都使用与我们相同的 IDE/插件集/文本编辑器。 (2认同)
  • @DenesPapp 代码编辑器并不重要,只要人们可以高效地使用它即可。除此之外,这只是个人喜好。您与在纸上编写代码的类比是不准确的,您无法在纸上执行代码,但可以执行在任何文本编辑器中编写的代码。 (2认同)
  • @perymimon 你可以,但你必须安装 `rxjs-compat` 包 https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/migration.md#rxjs-v5x-to-v6 -更新指南 (2认同)

Jua*_*des 12

我想出的一个很好的总结是:

它将流操作(映射、过滤、减少...)与核心功能(订阅、管道)分离。通过管道操作而不是链接,它不会污染 Observable 的原型,从而更容易进行摇树。

https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md#why

用于点链的修补运算符的问题是:

任何导入补丁运算符的库都会为该库的所有使用者增加 Observable.prototype,从而创建盲目依赖。如果图书馆删除了他们的使用,他们会在不知不觉中破坏其他人。使用 pipeables,您必须将所需的运算符导入到使用它们的每个文件中。

直接修补到原型上的操作符不能被 rollup 或 webpack 等工具“摇树”。可管道操作符将是直接从模块中提取的函数。

任何类型的构建工具或 lint 规则都无法可靠地检测到应用程序中导入的未使用运算符。这意味着您可能会导入扫描,但停止使用它,它仍会被添加到您的输出包中。对于可管道操作符,如果您不使用它,lint 规则可以为您挑选它。

功能组合很棒。构建您自己的自定义运算符变得更加容易,现在它们的工作方式和外观与 rxjs 中的所有其他运算符一样。您不再需要扩展 Observable 或覆盖提升。


Don*_*nga 12

有什么不同? 正如您在示例中看到的,主要区别在于提高源代码的可读性。您的示例中只有两个函数,但是想象一下,如果有十几个函数?然后它会像

function1().function2().function3().function4()

它真的变得丑陋且难以阅读,尤其是当您在函数内部进行填充时。最重要的是,某些编辑器(如 Visual Studio 代码)不允许超过 140 行的长度。但如果它像下面这样。

Observable.pipe(
function1(),
function2(),
function3(),
function4()
)
Run Code Online (Sandbox Code Playgroud)

这大大提高了可读性。

如果没有区别,为什么会存在函数管道? PIPE() 函数的目的是将所有接受和返回 observable 的函数集中在一起。它最初需要一个 observable,然后该 observable 被它内部使用的每个函数在整个 pipe() 函数中使用。

第一个函数接收observable,处理它,修改它的值,然后传递给下一个函数,然后next函数接收第一个函数的输出observable,处理它,并传递给下一个函数,然后继续直到所有的函数在 pipe() 函数内部使用那个 observable,最后你有处理过的 observable。最后,您可以使用 subscribe() 函数执行 observable 以从中提取值。请记住,原始 observable 中的值不会改变。!! 

为什么这些函数需要不同的导入? 导入取决于函数在 rxjs 包中的指定位置。它是这样的。所有模块都存储在 Angular 的 node_modules 文件夹中。从“模块”导入{类};

我们以下面的代码为例。我刚刚在stackblitz中写了它。因此不会自动生成或从其他地方复制任何内容。当您也可以阅读 rxjs 文档中的内容时,我认为没有必要复制它。我假设您在这里问了这个问题,因为您不了解文档。 

  • 有从各自模块导入的 pipe、observable、of、map 类。 
  • 在类的主体中,我使用了代码中看到的 Pipe() 函数。 
  • Of() 函数返回一个 observable,它在订阅时按顺序发出数字。

  • Observable 尚未订阅。

  • 当你像 Observable.pipe() 一样使用它时, pipe() 函数使用给定的 Observable 作为输入。

  • 第一个函数,map() 函数使用那个 Observable,处理它,将处理过的 Observable 返回给 pipe() 函数,

  • 然后将处理过的 Observable 交给下一个函数,如果有的话,

  • 一直这样,直到所有函数都处理 Observable,

  • 最后 Observable 被 pipe() 函数返回给一个变量,在下面的例子中它的 obs.

现在 Observable 中的事情是,只要观察者没有订阅它,它就不会发出任何值。所以我用subscribe()函数订阅了这个Observable,然后就订阅了。of() 函数开始发出值,然后通过 pipe() 函数对其进行处理,最后得到最终结果,例如从 of() 函数中取出 1,在 map() 函数中加 1,并返回。您可以将该值作为 subscribe( function ( argument ) {} ) 函数内部的参数获取。

如果你想打印它,然后使用作为

subscribe( function (argument) {
    console.log(argument)
   } 
)
Run Code Online (Sandbox Code Playgroud)
    import { Component, OnInit } from '@angular/core';
    import { pipe } from 'rxjs';
    import { Observable, of } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent implements OnInit  {
    
      obs = of(1,2,3).pipe(
      map(x => x + 1),
      ); 
    
      constructor() { }
    
      ngOnInit(){  
        this.obs.subscribe(value => console.log(value))
      }
    }

Run Code Online (Sandbox Code Playgroud)

https://stackblitz.com/edit/angular-ivy-plifkg

  • 不知道。我更喜欢第一种方法。对我来说,它看起来更干净,也更符合逻辑。 (3认同)
  • 我也一样。fn().fn().fn() 看起来不错。使用管道有很多充分的理由,但我认为可读性并没有真正提高。我更喜欢的一个论点是,点表示法适用于对象属性和函数,并且在这种情况下是人为的。从数学上讲,该函数应该是 fn4(fn3(fn2(fn1()))) 现在 _That_ 很难看。 (2认同)
  • @DonDilanga pipable 运算符是将可观察值作为输入的函数,它返回另一个可观察值。先前的可观察值保持不变。- 这对管道操作员来说很重要。 (2认同)

Yil*_*maz 7

我是这样解释可观察的:

您需要根据天气状况制定计划,以便打开收音机并收听 24/7 广播天气状况的天气频道。在这种情况下,响应不是得到单一响应,而是持续进行。这个响应就像一个可观察的订阅。可观察的是“天气”,订阅的是“让您了解最新情况的无线电信号”。只要您的收音机打开,您就会收到所有可用的更新。在关闭收音机之前,您不会错过任何信息。

我说天气是可以观察到的,但你听的是广播而不是天气。所以无线电也是可观察的。天气预报员所说的就是气象学家发给他的天气预报的功能。气象学家所写的是来自气象站的数据的函数。来自气象站的数据是与其连接的所有仪器(气压计、风向仪、风速计)的函数,而这些仪器是天气本身的函数。

整个过程中至少有5个可观察点。在这个过程中,有两种类型的可观察量。源可观察值和输出可观察值。在此示例中,天气是“可观测源”,无线电是“可观测输出”。介于两者之间的所有内容都代表PIPE FUNCTION.

管道函数是让源可观察量对其执行操作以提供输出可观察量,并且所有这些操作都发生在内部。所有操作都处理可观察量本身


Cha*_*ghe 6

管道法

所有这些看起来很酷,但仍然很冗长。好吧,多亏了RxJS 5.5可观察对象,实例现在有了管道方法,可以通过使用我们所有的纯函数运算符调用管道来清理上面的代码:

这意味着什么?

这意味着您先前在observable实例上使用的任何运算符都可以作为的纯函数使用rxjs/operators。这使得构建操作员组合或重用操作员变得非常容易,而不必诉诸各种编程体操,在这些体操中,您必须创建自定义的可观察的扩展Observable,然后覆盖提升以创建自己的自定义内容。

const { Observable } = require('rxjs/Rx')
const { filter, map, reduce,  } = require('rxjs/operators')
const { pipe } = require('rxjs/Rx')

const filterOutEvens = filter(x => x % 2)
const doubleBy = x => map(value => value * x);
const sum = reduce((acc, next) => acc + next, 0);
const source$ = Observable.range(0, 10)

source$.pipe(
  filterOutEvens, 
  doubleBy(2), 
  sum)
  .subscribe(console.log); // 50
Run Code Online (Sandbox Code Playgroud)