What is pipe for in rxJS

2020-01-30 06:22发布

问题:

I think I have the base concept, but there are some obscurities

So in general this is how I use an observable:

observable.subscribe(x => {

})

If I want to filter data I can use this:

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

})

I can also do this:

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

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

})

So my questions are:

  1. What is the difference?
  2. If there is no difference, why the function pipe exists?
  3. Why those functions need different imports?

回答1:

The "pipable" (former "lettable") operators is the current and recommended way of using operators since RxJS 5.5.

I strongly recommend you to read the official documentation https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md

The main difference is that it's easier to make custom operators and that it's better treeshakable while not altering some global Observable object that could possible make collisions if two different parties wanted to create an operator of the same name.

Using separate import statement for each operator 'rxjs/add/operator/first' was a way to make smaller app bundles. By importing only operators you need instead of the entire RxJS library you can significantly reduce the total bundle size. However the compiler can't know if you imported 'rxjs/add/operator/first' because you really need it in you code or you just forgot to remove it when refactoring your code. That's one of the advantages of using pipable operators where unused imports are ignored automatically.



回答2:

A good summary I've come up with is:

It decouples the streaming operations (map, filter, reduce...) from the core functionality(subscribing, piping). By piping operations instead of chaining, it doesn't pollute the prototype of Observable making it easier to do tree shaking.

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

Problems with the patched operators for dot-chaining are:

Any library that imports a patch operator will augment the Observable.prototype for all consumers of that library, creating blind dependencies. If the library removes their usage, they unknowingly break everyone else. With pipeables, you have to import the operators you need into each file you use them in.

Operators patched directly onto the prototype are not "tree-shakeable" by tools like rollup or webpack. Pipeable operators will be as they are just functions pulled in from modules directly.

Unused operators that are being imported in apps cannot be detected reliably by any sort of build tooling or lint rule. That means that you might import scan, but stop using it, and it's still being added to your output bundle. With pipeable operators, if you're not using it, a lint rule can pick it up for you.

Functional composition is awesome. Building your own custom operators becomes much, much easier, and now they work and look just like all other operators from rxjs. You don't need to extend Observable or override lift anymore.



回答3:

The pipe method Blog

All this looks cool but its still very verbose. Well, thanks to RxJS 5.5 observables now have a pipe method available on the instances allowing you to clean up the code above by calling pipe with all our pure functions operators:

What does that mean?

That means that any operators you previously used on the instance of observable are available as pure functions under rxjs/operators. This makes building a composition of operators or re-using operators becomes really easy, without having to resort to all sorts of programming gymnastics where you have to create a custom observable extending Observable, then overwrite lift just to make your own custom thing.

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