RxJS v5.x至v6更新指南

2020-10-09 15:01 更新

RxJS v6 到了! 盡管這是主要的版本更改(從 5.x 更改為 6.x), 我們已經(jīng)進(jìn)行了大量工作,以將艱巨的更改保持在最低限度。 在大多數(shù)情況下,這允許應(yīng)用程序和庫(kù)開(kāi)發(fā)人員逐步更新 并使用 RxJS v6,無(wú)需對(duì)其代碼進(jìn)行任何修改。

向后兼容層簡(jiǎn)化了更新過(guò)程,使您可以保持 應(yīng)用程序在您按自己的進(jìn)度處理大多數(shù)代碼更改時(shí)仍在運(yùn)行。 整個(gè)過(guò)程可以分階段進(jìn)行:

  1. 更新到最新版本的 RxJS 5.5,并確保您已解決由錯(cuò)誤修復(fù)引起的所有問(wèn)題。

  1. 安裝 RxJS v6和 向后兼容軟件包 rxjs-compat。

  1. 如果您的應(yīng)用受到 的一些 重大更改 未涵蓋 rxjs-compat的影響,請(qǐng)根據(jù)下面提供的說(shuō)明更新受影響的代碼。

  1. 最終,您將需要 刪除兼容性層以完成對(duì) RxJS v6 的更新。 這樣做將大大減小應(yīng)用程序的大小。

要重構(gòu) TypeScript 代碼,使其不依賴(lài)于 rxjs-compat,可以使用 rxjs-tslint。

npm i -g rxjs-tslint
rxjs-5-to-6-migrate -p [path/to/tsconfig.json]

  1. 在 RxJS v7 發(fā)行版之前,您將需要?jiǎng)h除和替換所有 不推薦使用的功能 。

向后兼容

為了最大程度地減少升級(jí)的影響,RxJS v6 發(fā)行了一個(gè)同級(jí)軟件包 軟件包 rxjs-compat,該 在 v6 和 v5 API 之間提供了一個(gè)兼容性層。 擁有現(xiàn)有應(yīng)用程序的大多數(shù)開(kāi)發(fā)人員都應(yīng)同時(shí)安裝 來(lái)升級(jí) rxjsrxjs-compat^ 6.0.0 ^ 6.0.0 :

npm install rxjs@6 rxjs-compat@6

有關(guān)此軟件包的詳細(xì)信息,請(qǐng)參見(jiàn)此處 .

兼容性軟件包會(huì)增加應(yīng)用程序的捆綁包大小,這就是為什么我們建議您在應(yīng)用程序和依賴(lài)項(xiàng)更新后立即將其刪除。 如果您使用的 Webpack 版本是 4.0.0之前,則此大小增加會(huì)加劇。

有關(guān)要?jiǎng)h除的更新內(nèi)容的完整說(shuō)明 rxjs-compat,請(qǐng)參閱下文 刪除兼容性層 。 還要注意,將應(yīng)用程序完全更新到 v6 可能會(huì)暴露以前未顯示的現(xiàn)有類(lèi)型錯(cuò)誤。

rxjs-compat 未涵蓋的重大更改

如果已安裝 rxjs-compat,則可能只需要立即解決兩個(gè)重大更改。

同步錯(cuò)誤處理

同步錯(cuò)誤處理( 放置對(duì) 的調(diào)用 Observable.subscribe()在 方法 try/catch不再支持 塊中 )。 如果使用了它,則必須使用 的 將其替換為異步錯(cuò)誤處理 error回調(diào) Observable.subscribe()方法中 。

TypeScript 原型運(yùn)算符

如果要在 TypeScript 中定義自己的原型運(yùn)算符并修改 Observable名稱(chēng)空間,則需要更改運(yùn)算符代碼才能編譯 TypeScript。 。 這是一種相對(duì)罕見(jiàn)的情況,可能只影響高級(jí) TypeScript 開(kāi)發(fā)人員。

取代同步錯(cuò)誤處理 以下示例顯示了在 內(nèi)預(yù)訂可觀(guān)察對(duì)象的代碼, try/catch塊 以便同步處理錯(cuò)誤:

try {
  source$.subscribe(nextFn, undefined, completeFn);
} catch (err) {
  handleError(err);
}

以下代碼通過(guò)定義以下內(nèi)容的錯(cuò)誤回調(diào)將其更新為異步處理錯(cuò)誤 Observable.subscribe()

source$.subscribe(nextFn, handleError, completeFn);

下一個(gè)示例顯示了一個(gè)依賴(lài)于同步錯(cuò)誤處理的測(cè)試:

it('should emit an error on subscription', () => {
  expect(source$.subscribe()).toThrow(Error, 'some message');
});

以下代碼顯示了如何更正測(cè)試以使用異步錯(cuò)誤處理:

it('should emit an error on subscription', (done) => {
  source$.subscribe({
    error(err) {
      expect(err.message).toEqual('some message');
    }
  });
});

TypeScript用戶(hù)定義的原型運(yùn)算符

以下示例顯示了您需要在用戶(hù)定義的原型運(yùn)算符中進(jìn)行的更改類(lèi)型,以便正確編譯TypeScript。

這是一個(gè)用戶(hù)定義的原型運(yùn)算符的示例r:

Observable.prototype.userDefined = function () {
  return new Observable((subscriber) => {
    this.subscribe({
      next(value) { subscriber.next(value); },
      error(err) { subscriber.error(err); },
      complete() { subscriber.complete(); },
   });
  });
};


source$.userDefined().subscribe();

要使此代碼在 v6 中正確編譯,請(qǐng)按如下所示進(jìn)行更改:

const userDefined = <T>() => (source: Observable<T>) => new Observable<T>((subscriber) => {
    source.subscribe({
      next(value) { subscriber.next(value); },
      error(err) { subscriber.error(err); },
      complete() { subscriber.complete(); },
   });
  });
});


source$.pipe(
  userDefined(),
)
.subscribe();

刪除兼容性層

如果使用從 v6 中刪除但 支持的功能,則 rxjs-compat軟件包 必須重構(gòu)或重寫(xiě)代碼以完成對(duì) v6 的更新。 以下功能區(qū)域取決于兼容性層:

  • 導(dǎo)入路徑已更改。
  • 操作員語(yǔ)法已更改為使用管道而不是鏈接。
  • 對(duì)可觀(guān)察對(duì)象進(jìn)行操作的類(lèi)已被函數(shù)替換。
  • 在具有 resultSelector 參數(shù)的函數(shù)中,大多數(shù)情況下已棄用該參數(shù),而對(duì)于兩個(gè)函數(shù)則將其刪除。 必須先更新已刪除的內(nèi)容,然后才能刪除兼容性層。

導(dǎo)入路徑

如果您是 TypeScript 開(kāi)發(fā)人員,建議您使用它 rxjs-tslint來(lái)重構(gòu)導(dǎo)入路徑。

對(duì)于 JavaScript 開(kāi)發(fā)人員,一般規(guī)則如下:

  1. rxjs: 創(chuàng)建方法,類(lèi)型,調(diào)度程序和實(shí)用程序

import { Observable, Subject, asapScheduler, pipe, of, from, interval, merge, fromEvent, SubscriptionLike, PartialObserver } from 'rxjs';

  1. rxjs / operators :所有可管道運(yùn)算符:

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

  1. rxjs / webSocket: Web 套接字主題實(shí)現(xiàn)

import { webSocket } from 'rxjs/webSocket';

  1. rxjs / ajax :Rx ajax 實(shí)現(xiàn)

import { ajax } from 'rxjs/ajax';    

  1. rxjs / testing :測(cè)試實(shí)用程序

import { TestScheduler } from 'rxjs/testing';    

操作符管道語(yǔ)法

鏈接運(yùn)算符的以前編碼風(fēng)格已被一個(gè)運(yùn)算符的結(jié)果傳遞給另一個(gè)運(yùn)算符的結(jié)果所取代。 5.5版中添加了可管道運(yùn)算符。

在刪除兼容性層之前,必須重構(gòu)代碼以?xún)H使用可管道運(yùn)算符。 對(duì)于 Typescript,該 tslint工具通過(guò)將轉(zhuǎn)換應(yīng)用于類(lèi)型正確的代碼來(lái)在某種程度上實(shí)現(xiàn)流程自動(dòng)化。

可觀(guān)察的類(lèi)

所有可觀(guān)察的類(lèi),以支持執(zhí)行與類(lèi)方法相同的操作的現(xiàn)有或新運(yùn)算符。 例如, ArrayObservable.create(myArray)可以由 替換 from(myArray)或 為new運(yùn)算符 fromArray()。

  • ConnectableObservable從直接利用隱藏在 V6 和只能通過(guò)運(yùn)營(yíng)商進(jìn)行訪(fǎng)問(wèn) multicast, publishpublishReplay,和 publishLast。
  • SubscribeOnObservable在 v6 中無(wú)法直接使用,并且只能通過(guò)operator進(jìn)行訪(fǎng)問(wèn) subscribeOn。
v6創(chuàng)建功能 v5類(lèi)
from ArrayLikeObservable
of 數(shù)組可觀(guān)察
bindCallback BoundCallbackObservable
bindNodeCallback BoundNodeCallbackObservable
defer 延遲可觀(guān)察
empty or EMPTY (constant) 空可觀(guān)察
throwError 錯(cuò)誤可觀(guān)察
forkJoin 前叉可觀(guān)察
fromEvent FromEventObservable
fromEventPattern FromEventPatternObservable
from FromObservable
generate GenerateObservable
iif IfObservable
interval 間隔可觀(guān)察
from IteratorObservable
NEVER (constant) 永不觀(guān)察
pairs 可觀(guān)察對(duì)
from 承諾可觀(guān)察
range 可觀(guān)察范圍
of 標(biāo)量可觀(guān)察
timer 計(jì)時(shí)器可觀(guān)察
using 使用可觀(guān)察

結(jié)果選擇器已刪除或已棄用

結(jié)果選擇器是一個(gè)很少有人使用的功能(在許多情況下沒(méi)有記錄),但是卻在代碼庫(kù)中增加了很大的膨脹。 如果使用它們,則需要 替換不連續(xù)的 resultSelector用外部結(jié)果選擇代碼 參數(shù)。

  • resultSelector對(duì)參數(shù) first()last()刪除 的 V6 發(fā)動(dòng)機(jī)。 如果使用了這些,則必須將代碼更新為在沒(méi)有 情況下運(yùn)行 rxjs-compat軟件包的 。
  • resultSelector已 了許多映射運(yùn)算符可用 參數(shù) 棄用 v6 ,并將實(shí)現(xiàn)重寫(xiě)為小得多。 它們將在沒(méi)有兼容軟件包的情況下繼續(xù)工作,但是必須在 v7 版本之前將其更換。

棄用

在 RxJS 發(fā)行 v7 之前,您將需要?jiǎng)h除和替換所有不贊成使用的功能。 以下區(qū)域包含不推薦使用的功能:

  • Observable.ifObservable.throw 這些方法已由 static 代替 iif()throwError()function 。 使用 rxjs-tslint 將方法調(diào)用與函數(shù)調(diào)用轉(zhuǎn)換。
  • “創(chuàng)造”運(yùn)營(yíng)商 下面的運(yùn)營(yíng)商已經(jīng)從移動(dòng) rxjs/operatorsrxjs中,其使用已經(jīng)改變:
  • merge
  • concat
  • combineLatest
  • race
  • zip
  • 結(jié)果選擇器

如何:轉(zhuǎn)換為管道語(yǔ)法

在將點(diǎn)鏈運(yùn)算符轉(zhuǎn)換為可管道運(yùn)算符之前,請(qǐng)確保從中導(dǎo)入所有使用的運(yùn)算符 rxjs/operators。 例如:

import { map, filter, catchError, mergeMap } from 'rxjs/operators';

更改了以下運(yùn)算符名稱(chēng),因?yàn)樗鼈兊狞c(diǎn)鏈名稱(chēng)是JavaScript中的保留字:

  • do-> tap
  • catch-> catchError
  • switch-> switchAll
  • finally-> finalize

要將點(diǎn)鏈運(yùn)算符轉(zhuǎn)換為可管道運(yùn)算符,請(qǐng)將所有運(yùn)算符 包裝在 符 pipe()從可觀(guān)察的源中 方法中,刪除點(diǎn),然后添加逗號(hào)以將每個(gè)運(yùn)算 傳遞 pipe()作為參數(shù) 。

例如,以下代碼使用鏈接:

source
 .map(x => x + x)
 .mergeMap(n => of(n + 1, n + 2)
   .filter(x => x % 1 == 0)
   .scan((acc, x) => acc + x, 0)
 )
 .catch(err => of('error found'))
 .subscribe(printResult);

轉(zhuǎn)換為管道:

source.pipe(
 map(x => x + x),
 mergeMap(n => of(n + 1, n + 2).pipe(
   filter(x => x % 1 == 0),
   scan((acc, x) => acc + x, 0),
 )),
 catchError(err => of('error found')),
).subscribe(printResult);

如何:轉(zhuǎn)換不建議使用的方法

Observable.if> iif()

Observable.if(test, a$, b$);


// becomes


iif(test, a$, b$);

Observable.error> throwError()

Observable.throw(new Error());


// becomes


throwError(new Error());

import { merge } from 'rxjs/operators';
a$.pipe(merge(b$, c$));


// becomes


import { merge } from 'rxjs';
merge(a$, b$, c$);

康卡特

import { merge } from 'rxjs/operators';
a$.pipe(merge(b$, c$));


// becomes


import { merge } from 'rxjs';
merge(a$, b$, c$);

結(jié)合最新

import { combineLatest } from 'rxjs/operators';
a$.pipe(combineLatest(b$, c$));


// becomes


import { combineLatest } from 'rxjs';
combineLatest(a$, b$, c$);

種族

import { race } from 'rxjs/operators';
a$.pipe(race(b$, c$));


// becomes


import { race } from 'rxjs';
race(a$, b$, c$);

zip

import { zip } from 'rxjs/operators';
a$.pipe(zip(b$, c$));


// becomes


import { zip } from 'rxjs';
zip(a$, b$, c$);

如何:結(jié)果選擇器遷移

在 RxJS v5.x 中,許多運(yùn)算符都有一個(gè)可選的 resultSelector 參數(shù),您可以在其中傳遞用于處理運(yùn)算結(jié)果的函數(shù)。

如果使用參數(shù),則必須通過(guò)將結(jié)果選擇功能移出原始操作員調(diào)用并將其應(yīng)用于調(diào)用結(jié)果的方式來(lái)更新代碼。

  • 該參數(shù) 已從 刪除 v6 中的first()和last()運(yùn)算符中 ,但 rxjs-compat 軟件包支持該參數(shù)。 您必須更新代碼才能刪除兼容性軟件包。
  • 該參數(shù) 已棄用 在以下運(yùn)算符中 ,并將在 v7 中刪除。 您必須先更新代碼,然后才能遷移到 v7。
    • mergeMap()
    • mergeMapTo()
    • concatMap()
    • concatMapTo()
    • switchMap
    • switchMapTo()
    • exhaustMap()
    • forkJoin()
    • 壓縮()
    • CombineLatest()
    • fromEvent()

第一()

  • 與 resultSelector(v5.x)

source.pipe(
 first(predicate, resultSelector, defaultValue)
)

  • 沒(méi)有 resultSelector(如果您不使用其中的索引):

source.pipe(
 first(predicate, defaultValue),
 map(resultSelector)
)

  • 沒(méi)有 resultSelector(如果您正在使用其中的索引)

source.pipe(
 map((v, i) => [v, i]),
 first(([v, i]) => predicate(v, i)),
 map(([v, i]) => resultSelector(v, i)),
)

持續(xù)()

  • 與 resultSelector(v5.x)

source.pipe(
 last(predicate, resultSelector, defaultValue)
)

  • 沒(méi)有 resultSelector(如果您不使用其中的索引):

source.pipe(
 last(predicate, defaultValue),
 map(resultSelector)
)

  • 沒(méi)有 resultSelector(如果您正在使用其中的索引)

source.pipe(
 map((v, i) => [v, i]),
 last(([v, i]) => predicate(v, i)),
 map(([v, i]) => resultSelector(v, i)),
)

mergeMap()

  • 與 resultSelector(v5.x)一起使用 注意:concurrency-limit 參數(shù)是可選的,為完整起見(jiàn)在此顯示。

source.pipe(
 mergeMap(fn1, fn2, concurrency)
)

  • 內(nèi)部地圖實(shí)現(xiàn)了沒(méi)有 resultSelector 的相同功能。

source.pipe(
 mergeMap((a, i) => fn1(a, i).pipe(
   map((b, ii) => fn2(a, b, i, ii))
 )),
 concurrency
)

mergeMapTo()

  • 與 resultSelector(v5.x)

source.pipe(
 mergeMapTo(a$, resultSelector)
)

  • 沒(méi)有 resultSelector

source.pipe(
 mergeMap((x, i) => a$.pipe(
   map((y, ii) => resultSelector(x, y, i, ii))
 )
)

concatMap()

  • 與 resultSelector (v5.x)

source.pipe(
 concatMap(fn1, fn2)
)

  • 內(nèi)部地圖實(shí)現(xiàn)了沒(méi)有 resultSelector 的相同功能:

source.pipe(
 concatMap((a, i) => fn1(a, i).pipe(
   map((b, ii) => fn2(a, b, i, ii))
 )
)

concatMapTo()

  • 與 resultSelector(v5.x)

source.pipe(
 concatMapTo(a$, resultSelector)
)

  • 沒(méi)有 resultSelector

source.pipe(
 concatMap((x, i) => a$.pipe(
   map((y, ii) => resultSelector(x, y, i, ii))
 )
)

switchMap()

  • 與 resultSelector(v5.x)

source.pipe(
 switchMap((a, i) => fn1(a, i).pipe(
   map((b, ii) => fn2(a, b, i, ii))
 )
)

  • 內(nèi)部地圖實(shí)現(xiàn)了沒(méi)有 resultSelector 的相同功能

source.pipe(
 switchMap((a, i) => fn1(a, i).pipe(
   map((b, ii) => fn2(a, b, i, ii))
 )
)

switchMapTo()

  • 與 resultSelector(v5.x)

source.pipe(
 switchMapTo(a$, resultSelector)
)

  • 沒(méi)有 resultSelector

source.pipe(
 switchMap((x, i) => a$.pipe(
   map((y, ii) => resultSelector(x, y, i, ii))
 )
)

exhaustMap()

  • 與 resultSelector(v5.x)

source.pipe(
 exhaustMap(fn1, fn2)
)

  • 內(nèi)部地圖實(shí)現(xiàn)了沒(méi)有 resultSelecto 的相同功能

source.pipe(
 exhaustMap((a, i) => fn1(a, i).pipe(
   map((b, ii) => fn2(a, b, i, ii))
 )
)

forkJoin()

  • 與 resultSelector(v5.x)

forkJoin(a$, b$, c$, resultSelector)
// or
forkJoin([a$, b$, c$], resultSelector)

  • 沒(méi)有 resultSelector

forkJoin(a$, b$, c$).pipe(
 map(x => resultSelector(...x))
)
// or
forkJoin([a$, b$, c$]).pipe(
 map(x => resultSelector(...x))
)

壓縮()

  • 與 resultSelector(v5.x)

zip(a$, b$, c$, resultSelector)
// or
zip([a$, b$, c$], resultSelector)

  • 沒(méi)有 resultSelector

zip(a$, b$, c$).pipe(
 map(x => resultSelector(...x))
)
// or
zip([a$, b$, c$]).pipe(
 map(x => resultSelector(...x))
)

combineLatest()

  • 與 resultSelector(v5.x)

combineLatest(a$, b$, c$, resultSelector)
// or
combineLatest([a$, b$, c$], resultSelector)

  • 沒(méi)有 resultSelector

combineLatest(a$, b$, c$).pipe(
 map(x => resultSelector(...x))
)
// or
combineLatest([a$, b$, c$]).pipe(
 map(x => resultSelector(...x))
)

fromEvent()

  • 與resultSelector(v5.x)

fromEvent(button, 'click', resultSelector)

  • 沒(méi)有 resultSelector

fromEvent(button, 'click').pipe(
 map(resultSelector)
)

UMD 模塊名稱(chēng)更改

在 RxJS v6.x 中,UMD 模塊名稱(chēng)已從Rx更改為 rxjs,以便與其他導(dǎo)入模塊名稱(chēng)對(duì)齊。

const rx= Rx;


rx.Observable.of(1,2,3).map(x => x + '!!!');


// becomes


const { of } = rxjs;
const { map } = rxjs.operators;


of(1,2,3).pipe(map(x => x + '!!!')); // etc
以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)