RxJS 管道運算符

2020-10-09 15:31 更新

從5.5版開始,我們已經(jīng)發(fā)布了“可移植運算符”,可以在 進行訪問 rxjs/operators(請注意復(fù)數(shù)的“運算符”)中 。 與 找到的“ patch”運算符相比,這些方法是僅引入所需運算符的更好方法 rxjs-compat打包中 。

注意 :使用 rxjsrxjs/operators不更改構(gòu)建過程可能會導(dǎo)致捆綁包增大。 請參閱 已知問題” 下面的“ 部分。

重命名運營商

由于使運算符獨立于 Observable 可用,因此運算符名稱不能與 JavaScript 關(guān)鍵字限制沖突。 因此,某些運算符的可移植版本的名稱已更改。 這些運算符是:

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

let運算符現(xiàn)在是 一部分, Observableas 的 pipe無法導(dǎo)入。

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

請參閱下面的“ 建立自己的運營商 ”。

toPromise()“操作員”已被刪除 因為運算符返回 Observable, 不是一個 Promise。 現(xiàn)在有一個 Observable.toPromise()實例方法。

為什么?

修補的點鏈運算符的問題是:

  1. 導(dǎo)入修補程序運算符的任何庫 增加 Observable.prototype都將為該庫的所有使用者 ,從而造成盲目依賴。 如果庫刪除了它們的用法,它們將在不知不覺中破壞其他所有人。 使用 pipeable,必須將所需的運算符導(dǎo)入使用它們的每個文件中。

  1. 直接 到原型的操作員無法 “搖晃樹”匯總 通過匯總或 Webpack 之類的工具 。 可管道運算符將像直接從模塊中引入的函數(shù)一樣。

應(yīng)用程序中導(dǎo)入的未使用的運算符無法通過任何類型的構(gòu)建工具或 lint 規(guī)則可靠地檢測到。 這意味著您可以導(dǎo)入 scan,但停止使用它,并且仍將其添加到您的輸出包中。 對于可管道運算符,如果您不使用它,則可以使用皮棉規(guī)則為您選擇它。

功能組成很棒。 建立您自己的自定義運算符變得容易得多,現(xiàn)在它們可以正常工作,并且看起來就像 rxjs 中的所有其他運算符一樣。 您不需要擴展 Observable 或覆蓋它 lift。

什么?

什么是管道運算符? 簡而言之,可以與當前 一起使用的函數(shù) let運算符 。 它曾經(jīng)是名稱(“ lettable”)的起源,但是這令人困惑,因此我們現(xiàn)在將它們稱為“ pipeable”,因為它們打算與 一起使用 pipe實用程序 。 可管道運算符基本上是任何返回帶有簽名的函數(shù)的函數(shù): <T, R>(source: Observable<T>) => Observable<R>。

有一個 的 pipe內(nèi)置 方法 Observable現(xiàn)在, Observable.prototype.pipe可以用來像與點鏈接所用的相似方式組成運算符(如下所示)。

pipe可以從導(dǎo)入實用程序功能 import { pipe } from 'rxjs';。 該 pipe功能可用于從其他管道運算符構(gòu)建可重用的管道運算符。 例如:

import { pipe } from 'rxjs';
import { map } from 'rxjs/operators';


const mapTwice = <T,R>(fn: (value: T, index: number) => R) => pipe(map(fn), map(fn));

用法

您可以在 下從一個位置引入所需的任何運算符 'rxjs/operators'復(fù)數(shù)! ) 。 還建議直接使用所需的 Observable 創(chuàng)建方法,如下所示 range

import { range } from 'rxjs';
import { map, filter, scan } from 'rxjs/operators';


const source$ = range(0, 10);


source$.pipe(
  filter(x => x % 2 === 0),
  map(x => x + x),
  scan((acc, x) => acc + x, 0)
)
.subscribe(x => console.log(x))


// Logs:
// 0
// 4
// 12
// 24
// 40

輕松建立自己的運營商

實際上,您 始終 來 可以使用 做到這一點, let... 但是構(gòu)建自己的運算符現(xiàn)在就像編寫函數(shù)一樣簡單。 注意,您可以將自定義運算符與其他 rxjs 運算符無縫組合在一起。

import { Observable, interval } from 'rxjs';
import { filter, map, take, toArray } from 'rxjs/operators';


/**
 * an operator that takes every Nth value
 */
const takeEveryNth = (n: number) => <T>(source: Observable<T>) =>
  new Observable<T>(observer => {
    let count = 0;
    return source.subscribe({
      next(x) {
        if (count++ % n === 0) observer.next(x);
      },
      error(err) { observer.error(err); },
      complete() { observer.complete(); }
    })
  });


/**
 * You can also use an existing operator like so
 */
const takeEveryNthSimple = (n: number) => <T>(source: Observable<T>) =>
  source.pipe(filter((value, index) => index % n === 0 ))


/**
 * And since pipeable operators return functions, you can further simplify like so
 */
const takeEveryNthSimplest = (n: number) => filter((value, index) => index % n === 0);


interval(1000).pipe(
  takeEveryNth(2),
  map(x => x + x),
  takeEveryNthSimple(3),
  map(x => x * x),
  takeEveryNthSimplest(4),
  take(3),
  toArray()
)
.subscribe(x => console.log(x));
// Logs:
// [0, 2304, 9216]

已知的問題

打字稿<2.4

在 TypeScript 2.3及更低版本中,由于無法在 TypeScript 2.4之前推斷類型,因此需要將類型添加到傳遞給運算符的函數(shù)中。 在 TypeScript 2.4中,類型將通過合成正確推斷。

TS 2.3 and under

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

TS 2.4 以上

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

建造和搖樹

從清單文件(或重新導(dǎo)出)導(dǎo)入時,應(yīng)用程序捆綁有時會增長。 現(xiàn)在可以從中導(dǎo)入可管道運算符 rxjs/operators,但是在不更改生成過程的情況下這樣做通常會導(dǎo)致應(yīng)用程序包更大。 這是因為默認情況下 rxjs/operators將解析為 rxjs 的 CommonJS 輸出。

為了使用新的管道運算符而不增加捆綁包的大小,您將需要更改 Webpack 配置。 這僅適用于 Webpack 3+,因為它依賴于 的新 ModuleConcatenationPluginWebpack 3 中 功能。

路徑映射

與 rxjs 5.5 一起發(fā)布的是帶有 ES5 和 ES2015 語言級別的 ECMAScript 模塊格式(導(dǎo)入和導(dǎo)出)的 rxjs 構(gòu)建。 您可以在 找到這些分布 node_modules/rxjs/_esm5和中 node_modules/rxjs/_esm2015(“ esm”代表 ECMAScript 模塊,數(shù)字“ 5”或“ 2015”代表 ES 語言級別)。 在應(yīng)用程序源代碼中,應(yīng)從導(dǎo)入 rxjs/operators,但在 Webpack 配置文件中,將需要將導(dǎo)入重新映射為 ESM5(或 ESM2015)版本。

如果是 require('rxjs/_esm5/path-mapping'),您將收到一個函數(shù),該函數(shù)返回鍵-值對的對象,將每個輸入映射到其在磁盤上的文件位置。 使用此映射,如下所示:

webpack.config.js

配置簡單:

const rxPaths = require('rxjs/_esm5/path-mapping');
const webpack = require('webpack');
const path = require('path');


module.exports = {
  entry: 'index.js',
  output: 'bundle.js',
  resolve: {
    // Use the "alias" key to resolve to an ESM distribution
    alias: rxPaths()
  },
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin()
  ]
};

更完整的配置(更接近實際場景):

const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const DashboardPlugin = require('webpack-dashboard/plugin');
const nodeEnv = process.env.NODE_ENV || 'development';
const isProd = nodeEnv === 'production';
const rxPaths = require('rxjs/_esm5/path-mapping');


var config = {
    devtool: isProd ? 'hidden-source-map' : 'cheap-eval-source-map',
    context: path.resolve('./src'),
    entry: {
        app: './index.ts',
        vendor: './vendor.ts'
    },
    output: {
        path: path.resolve('./dist'),
        filename: '[name].bundle.js',
        sourceMapFilename: '[name].map',
        devtoolModuleFilenameTemplate: function (info) {
            return "file:///" + info.absoluteResourcePath;
        }
    },
    module: {
        rules: [
            { enforce: 'pre', test: /\.ts$|\.tsx$/, exclude: ["node_modules"], loader: 'ts-loader' },
            { test: /\.html$/, loader: "html" },
            { test: /\.css$/, loaders: ['style', 'css'] }
        ]
    },
    resolve: {
        extensions: [".ts", ".js"],
        modules: [path.resolve('./src'), 'node_modules'],
        alias: rxPaths()
    },
    plugins: [
        new webpack.DefinePlugin({
            'process.env': { // eslint-disable-line quote-props
                NODE_ENV: JSON.stringify(nodeEnv)
            }
        }),
        new webpack.HashedModuleIdsPlugin(),
        new webpack.optimize.ModuleConcatenationPlugin(),
        new HtmlWebpackPlugin({
            title: 'Typescript Webpack Starter',
            template: '!!ejs-loader!src/index.html'
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            minChunks: Infinity,
            filename: 'vendor.bundle.js'
        }),
        new webpack.optimize.UglifyJsPlugin({
            mangle: false,
            compress: { warnings: false, pure_getters: true, passes: 3, screw_ie8: true, sequences: false },
            output: { comments: false, beautify: true },
            sourceMap: false
        }),
        new DashboardPlugin(),
        new webpack.LoaderOptionsPlugin({
            options: {
                tslint: {
                    emitErrors: true,
                    failOnHint: true
                }
            }
        })
    ]
};


module.exports = config;
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號