RxJS 管道运算符

2020-10-09 15:31 更新

从5.5版开始,我们已经发布了“可移植运算符”,可以在 进行访问 rxjs/operators(请注意复数的“运算符”)中 。 与 找到的“ patch”运算符相比,这些方法是仅引入所需运算符的更好方法 rxjs-compat打包中 。

注意 :使用 rxjsrxjs/operators不更改构建过程可能会导致捆绑包增大。 请参阅 已知问题” 下面的“ 部分。

重命名运营商

由于使运算符独立于 Observable 可用,因此运算符名称不能与 JavaScript 关键字限制冲突。 因此,某些运算符的可移植版本的名称已更改。 这些运算符是:

  1. do-> tap
  2. catch-> catchError
  3. switch-> switchAll
  4. finally-> finalize

let运算符现在是 一部分, Observableas 的 pipe无法导入。

  1. source$.let(myOperator) -> source$.pipe(myOperator)

请参阅下面的“ 建立自己的运营商 ”。

toPromise()“操作员”已被删除 因为运算符返回 Observable, 不是一个 Promise。 现在有一个 Observable.toPromise()实例方法。

为什么?

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

  1. 导入修补程序运算符的任何库 增加 Observable.prototype都将为该库的所有使用者 ,从而造成盲目依赖。 如果库删除了它们的用法,它们将在不知不觉中破坏其他所有人。 使用 pipeable,必须将所需的运算符导入使用它们的每个文件中。

  1. 直接 到原型的操作员无法 “摇晃树”汇总 通过汇总或 Webpack 之类的工具 。 可管道运算符将像直接从模块中引入的函数一样。

应用程序中导入的未使用的运算符无法通过任何类型的构建工具或 lint 规则可靠地检测到。 这意味着您可以导入 scan,但停止使用它,并且仍将其添加到您的输出包中。 对于可管道运算符,如果您不使用它,则可以使用皮棉规则为您选择它。

功能组成很棒。 建立您自己的自定义运算符变得容易得多,现在它们可以正常工作,并且看起来就像 rxjs 中的所有其他运算符一样。 您不需要扩展 Observable 或覆盖它 lift

什么?

什么是管道运算符? 简而言之,可以与当前 一起使用的函数 let运算符 。 它曾经是名称(“ lettable”)的起源,但是这令人困惑,因此我们现在将它们称为“ pipeable”,因为它们打算与 一起使用 pipe实用程序 。 可管道运算符基本上是任何返回带有签名的函数的函数: <T, R>(source: Observable<T>) => Observable<R>

有一个 的 pipe内置 方法 Observable现在, Observable.prototype.pipe可以用来像与点链接所用的相似方式组成运算符(如下所示)。

pipe可以从导入实用程序功能 import { pipe } from 'rxjs';。 该 pipe功能可用于从其他管道运算符构建可重用的管道运算符。 例如:

  1. import { pipe } from 'rxjs';
  2. import { map } from 'rxjs/operators';
  3. const mapTwice = <T,R>(fn: (value: T, index: number) => R) => pipe(map(fn), map(fn));

用法

您可以在 下从一个位置引入所需的任何运算符 'rxjs/operators'复数! ) 。 还建议直接使用所需的 Observable 创建方法,如下所示 range

  1. import { range } from 'rxjs';
  2. import { map, filter, scan } from 'rxjs/operators';
  3. const source$ = range(0, 10);
  4. source$.pipe(
  5. filter(x => x % 2 === 0),
  6. map(x => x + x),
  7. scan((acc, x) => acc + x, 0)
  8. )
  9. .subscribe(x => console.log(x))
  10. // Logs:
  11. // 0
  12. // 4
  13. // 12
  14. // 24
  15. // 40

轻松建立自己的运营商

实际上,您 始终 来 可以使用 做到这一点, let... 但是构建自己的运算符现在就像编写函数一样简单。 注意,您可以将自定义运算符与其他 rxjs 运算符无缝组合在一起。

  1. import { Observable, interval } from 'rxjs';
  2. import { filter, map, take, toArray } from 'rxjs/operators';
  3. /**
  4. * an operator that takes every Nth value
  5. */
  6. const takeEveryNth = (n: number) => <T>(source: Observable<T>) =>
  7. new Observable<T>(observer => {
  8. let count = 0;
  9. return source.subscribe({
  10. next(x) {
  11. if (count++ % n === 0) observer.next(x);
  12. },
  13. error(err) { observer.error(err); },
  14. complete() { observer.complete(); }
  15. })
  16. });
  17. /**
  18. * You can also use an existing operator like so
  19. */
  20. const takeEveryNthSimple = (n: number) => <T>(source: Observable<T>) =>
  21. source.pipe(filter((value, index) => index % n === 0 ))
  22. /**
  23. * And since pipeable operators return functions, you can further simplify like so
  24. */
  25. const takeEveryNthSimplest = (n: number) => filter((value, index) => index % n === 0);
  26. interval(1000).pipe(
  27. takeEveryNth(2),
  28. map(x => x + x),
  29. takeEveryNthSimple(3),
  30. map(x => x * x),
  31. takeEveryNthSimplest(4),
  32. take(3),
  33. toArray()
  34. )
  35. .subscribe(x => console.log(x));
  36. // Logs:
  37. // [0, 2304, 9216]

已知的问题

打字稿<2.4

在 TypeScript 2.3及更低版本中,由于无法在 TypeScript 2.4之前推断类型,因此需要将类型添加到传递给运算符的函数中。 在 TypeScript 2.4中,类型将通过合成正确推断。

TS 2.3 and under

  1. range(0, 10).pipe(
  2. map((n: number) => n + '!'),
  3. map((s: string) => 'Hello, ' + s),
  4. ).subscribe(x => console.log(x))

TS 2.4 以上

  1. range(0, 10).pipe(
  2. map(n => n + '!'),
  3. map(s => 'Hello, ' + s),
  4. ).subscribe(x => console.log(x))

建造和摇树

从清单文件(或重新导出)导入时,应用程序捆绑有时会增长。 现在可以从中导入可管道运算符 rxjs/operators,但是在不更改生成过程的情况下这样做通常会导致应用程序包更大。 这是因为默认情况下 rxjs/operators将解析为 rxjs 的 CommonJS 输出。

为了使用新的管道运算符而不增加捆绑包的大小,您将需要更改 Webpack 配置。 这仅适用于 Webpack 3+,因为它依赖于 的新 ModuleConcatenationPluginWebpack 3 中 功能。

路径映射

与 rxjs 5.5 一起发布的是带有 ES5 和 ES2015 语言级别的 ECMAScript 模块格式(导入和导出)的 rxjs 构建。 您可以在 找到这些分布 node_modules/rxjs/_esm5和中 node_modules/rxjs/_esm2015(“ esm”代表 ECMAScript 模块,数字“ 5”或“ 2015”代表 ES 语言级别)。 在应用程序源代码中,应从导入 rxjs/operators,但在 Webpack 配置文件中,将需要将导入重新映射为 ESM5(或 ESM2015)版本。

如果是 require('rxjs/_esm5/path-mapping'),您将收到一个函数,该函数返回键-值对的对象,将每个输入映射到其在磁盘上的文件位置。 使用此映射,如下所示:

webpack.config.js

配置简单:

  1. const rxPaths = require('rxjs/_esm5/path-mapping');
  2. const webpack = require('webpack');
  3. const path = require('path');
  4. module.exports = {
  5. entry: 'index.js',
  6. output: 'bundle.js',
  7. resolve: {
  8. // Use the "alias" key to resolve to an ESM distribution
  9. alias: rxPaths()
  10. },
  11. plugins: [
  12. new webpack.optimize.ModuleConcatenationPlugin()
  13. ]
  14. };

更完整的配置(更接近实际场景):

  1. const webpack = require('webpack');
  2. const path = require('path');
  3. const HtmlWebpackPlugin = require('html-webpack-plugin');
  4. const DashboardPlugin = require('webpack-dashboard/plugin');
  5. const nodeEnv = process.env.NODE_ENV || 'development';
  6. const isProd = nodeEnv === 'production';
  7. const rxPaths = require('rxjs/_esm5/path-mapping');
  8. var config = {
  9. devtool: isProd ? 'hidden-source-map' : 'cheap-eval-source-map',
  10. context: path.resolve('./src'),
  11. entry: {
  12. app: './index.ts',
  13. vendor: './vendor.ts'
  14. },
  15. output: {
  16. path: path.resolve('./dist'),
  17. filename: '[name].bundle.js',
  18. sourceMapFilename: '[name].map',
  19. devtoolModuleFilenameTemplate: function (info) {
  20. return "file:///" + info.absoluteResourcePath;
  21. }
  22. },
  23. module: {
  24. rules: [
  25. { enforce: 'pre', test: /\.ts$|\.tsx$/, exclude: ["node_modules"], loader: 'ts-loader' },
  26. { test: /\.html$/, loader: "html" },
  27. { test: /\.css$/, loaders: ['style', 'css'] }
  28. ]
  29. },
  30. resolve: {
  31. extensions: [".ts", ".js"],
  32. modules: [path.resolve('./src'), 'node_modules'],
  33. alias: rxPaths()
  34. },
  35. plugins: [
  36. new webpack.DefinePlugin({
  37. 'process.env': { // eslint-disable-line quote-props
  38. NODE_ENV: JSON.stringify(nodeEnv)
  39. }
  40. }),
  41. new webpack.HashedModuleIdsPlugin(),
  42. new webpack.optimize.ModuleConcatenationPlugin(),
  43. new HtmlWebpackPlugin({
  44. title: 'Typescript Webpack Starter',
  45. template: '!!ejs-loader!src/index.html'
  46. }),
  47. new webpack.optimize.CommonsChunkPlugin({
  48. name: 'vendor',
  49. minChunks: Infinity,
  50. filename: 'vendor.bundle.js'
  51. }),
  52. new webpack.optimize.UglifyJsPlugin({
  53. mangle: false,
  54. compress: { warnings: false, pure_getters: true, passes: 3, screw_ie8: true, sequences: false },
  55. output: { comments: false, beautify: true },
  56. sourceMap: false
  57. }),
  58. new DashboardPlugin(),
  59. new webpack.LoaderOptionsPlugin({
  60. options: {
  61. tslint: {
  62. emitErrors: true,
  63. failOnHint: true
  64. }
  65. }
  66. })
  67. ]
  68. };
  69. module.exports = config;
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号