RxJS v5.x至v6更新指南
RxJS v6 到了! 尽管这是主要的版本更改(从 5.x 更改为 6.x), 我们已经进行了大量工作,以将艰巨的更改保持在最低限度。 在大多数情况下,这允许应用程序和库开发人员逐步更新 并使用 RxJS v6,无需对其代码进行任何修改。
向后兼容层简化了更新过程,使您可以保持 应用程序在您按自己的进度处理大多数代码更改时仍在运行。 整个过程可以分阶段进行:
- 更新到最新版本的 RxJS 5.5,并确保您已解决由错误修复引起的所有问题。
- 安装 RxJS v6和 向后兼容软件包
rxjs-compat
。
- 如果您的应用受到 的一些 重大更改 未涵盖
rxjs-compat
的影响,请根据下面提供的说明更新受影响的代码。
- 最终,您将需要 删除兼容性层以完成对 RxJS v6 的更新。 这样做将大大减小应用程序的大小。
要重构 TypeScript 代码,使其不依赖于 rxjs-compat,可以使用 rxjs-tslint
。
npm i -g rxjs-tslint
rxjs-5-to-6-migrate -p [path/to/tsconfig.json]
- 在 RxJS v7 发行版之前,您将需要删除和替换所有 不推荐使用的功能 。
向后兼容
为了最大程度地减少升级的影响,RxJS v6 发行了一个同级软件包 软件包 rxjs-compat
,该 在 v6 和 v5 API 之间提供了一个兼容性层。 拥有现有应用程序的大多数开发人员都应同时安装 来升级 rxjs
和 rxjs-compat
^ 6.0.0 ^ 6.0.0 :
npm install rxjs@6 rxjs-compat@6
有关此软件包的详细信息,请参见此处 .
兼容性软件包会增加应用程序的捆绑包大小,这就是为什么我们建议您在应用程序和依赖项更新后立即将其删除。 如果您使用的 Webpack 版本是 4.0.0之前,则此大小增加会加剧。
有关要删除的更新内容的完整说明 rxjs-compat
,请参阅下文 删除兼容性层 。 还要注意,将应用程序完全更新到 v6 可能会暴露以前未显示的现有类型错误。
rxjs-compat 未涵盖的重大更改
如果已安装 rxjs-compat
,则可能只需要立即解决两个重大更改。
同步错误处理
同步错误处理( 放置对 的调用 Observable.subscribe()
在 方法 try/catch
不再支持 块中 )。 如果使用了它,则必须使用 的 将其替换为异步错误处理 error
回调 Observable.subscribe()
方法中 。
TypeScript 原型运算符
如果要在 TypeScript 中定义自己的原型运算符并修改 Observable
名称空间,则需要更改运算符代码才能编译 TypeScript。 。 这是一种相对罕见的情况,可能只影响高级 TypeScript 开发人员。
取代同步错误处理 以下示例显示了在 内预订可观察对象的代码, try/catch
块 以便同步处理错误:
try {
source$.subscribe(nextFn, undefined, completeFn);
} catch (err) {
handleError(err);
}
以下代码通过定义以下内容的错误回调将其更新为异步处理错误 Observable.subscribe()
:
source$.subscribe(nextFn, handleError, completeFn);
下一个示例显示了一个依赖于同步错误处理的测试:
it('should emit an error on subscription', () => {
expect(source$.subscribe()).toThrow(Error, 'some message');
});
以下代码显示了如何更正测试以使用异步错误处理:
it('should emit an error on subscription', (done) => {
source$.subscribe({
error(err) {
expect(err.message).toEqual('some message');
}
});
});
TypeScript用户定义的原型运算符
以下示例显示了您需要在用户定义的原型运算符中进行的更改类型,以便正确编译TypeScript。
这是一个用户定义的原型运算符的示例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 中正确编译,请按如下所示进行更改:
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
软件包 必须重构或重写代码以完成对 v6 的更新。 以下功能区域取决于兼容性层:
- 导入路径已更改。
- 操作员语法已更改为使用管道而不是链接。
- 对可观察对象进行操作的类已被函数替换。
- 在具有 resultSelector 参数的函数中,大多数情况下已弃用该参数,而对于两个函数则将其删除。 必须先更新已删除的内容,然后才能删除兼容性层。
导入路径
如果您是 TypeScript 开发人员,建议您使用它 rxjs-tslint
来重构导入路径。
对于 JavaScript 开发人员,一般规则如下:
- rxjs: 创建方法,类型,调度程序和实用程序
import { Observable, Subject, asapScheduler, pipe, of, from, interval, merge, fromEvent, SubscriptionLike, PartialObserver } from 'rxjs';
- rxjs / operators :所有可管道运算符:
import { map, filter, scan } from 'rxjs/operators';
- rxjs / webSocket: Web 套接字主题实现
import { webSocket } from 'rxjs/webSocket';
- rxjs / ajax :Rx ajax 实现
import { ajax } from 'rxjs/ajax';
- rxjs / testing :测试实用程序
import { TestScheduler } from 'rxjs/testing';
操作符管道语法
链接运算符的以前编码风格已被一个运算符的结果传递给另一个运算符的结果所取代。 5.5版中添加了可管道运算符。
在删除兼容性层之前,必须重构代码以仅使用可管道运算符。 对于 Typescript,该 tslint
工具通过将转换应用于类型正确的代码来在某种程度上实现流程自动化。
可观察的类
所有可观察的类,以支持执行与类方法相同的操作的现有或新运算符。 例如, ArrayObservable.create(myArray)
可以由 替换 from(myArray)
或 为new运算符 fromArray()
。
ConnectableObservable
从直接利用隐藏在 V6 和只能通过运营商进行访问multicast
,publish
,publishReplay
,和publishLast
。SubscribeOnObservable
在 v6 中无法直接使用,并且只能通过operator进行访问subscribeOn
。
v6创建功能 | v5类 |
---|---|
from | ArrayLikeObservable |
of | 数组可观察 |
bindCallback | BoundCallbackObservable |
bindNodeCallback | BoundNodeCallbackObservable |
defer | 延迟可观察 |
empty or EMPTY (constant) | 空可观察 |
throwError | 错误可观察 |
forkJoin | 前叉可观察 |
fromEvent | FromEventObservable |
fromEventPattern | FromEventPatternObservable |
from | FromObservable |
generate | GenerateObservable |
iif | IfObservable |
interval | 间隔可观察 |
from | IteratorObservable |
NEVER (constant) | 永不观察 |
pairs | 可观察对 |
from | 承诺可观察 |
range | 可观察范围 |
of | 标量可观察 |
timer | 计时器可观察 |
using | 使用可观察 |
结果选择器已删除或已弃用
结果选择器是一个很少有人使用的功能(在许多情况下没有记录),但是却在代码库中增加了很大的膨胀。 如果使用它们,则需要 替换不连续的 resultSelector
用外部结果选择代码 参数。
- 在
resultSelector
对参数first()
和last()
被 删除 的 V6 发动机。 如果使用了这些,则必须将代码更新为在没有 情况下运行rxjs-compat
软件包的 。 - 的
resultSelector
已 了许多映射运算符可用 参数 弃用 v6 ,并将实现重写为小得多。 它们将在没有兼容软件包的情况下继续工作,但是必须在 v7 版本之前将其更换。
弃用
在 RxJS 发行 v7 之前,您将需要删除和替换所有不赞成使用的功能。 以下区域包含不推荐使用的功能:
Observable.if
和Observable.throw
这些方法已由 static 代替iif()
和throwError()
function 。 使用 rxjs-tslint 将方法调用与函数调用转换。- “创造”运营商 下面的运营商已经从移动
rxjs/operators
到rxjs
中,其使用已经改变: merge
concat
combineLatest
race
zip
- 结果选择器
如何:转换为管道语法
在将点链运算符转换为可管道运算符之前,请确保从中导入所有使用的运算符 rxjs/operators
。 例如:
import { map, filter, catchError, mergeMap } from 'rxjs/operators';
更改了以下运算符名称,因为它们的点链名称是JavaScript中的保留字:
do
->tap
catch
->catchError
switch
->switchAll
finally
->finalize
要将点链运算符转换为可管道运算符,请将所有运算符 包装在 符 pipe()
从可观察的源中 方法中,删除点,然后添加逗号以将每个运算 传递 pipe()
作为参数 。
例如,以下代码使用链接:
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);
转换为管道:
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);
如何:转换不建议使用的方法
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$);
结合最新
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$);
如何:结果选择器迁移
在 RxJS v5.x 中,许多运算符都有一个可选的 resultSelector 参数,您可以在其中传递用于处理运算结果的函数。
如果使用参数,则必须通过将结果选择功能移出原始操作员调用并将其应用于调用结果的方式来更新代码。
- 该参数 已从 删除 v6 中的first()和last()运算符中 ,但 rxjs-compat 软件包支持该参数。 您必须更新代码才能删除兼容性软件包。
- 该参数 已弃用 在以下运算符中 ,并将在 v7 中删除。 您必须先更新代码,然后才能迁移到 v7。
- mergeMap()
- mergeMapTo()
- concatMap()
- concatMapTo()
- switchMap
- switchMapTo()
- exhaustMap()
- forkJoin()
- 压缩()
- CombineLatest()
- fromEvent()
第一()
- 与 resultSelector(v5.x)
source.pipe(
first(predicate, resultSelector, defaultValue)
)
- 没有 resultSelector(如果您不使用其中的索引):
source.pipe(
first(predicate, defaultValue),
map(resultSelector)
)
- 没有 resultSelector(如果您正在使用其中的索引)
source.pipe(
map((v, i) => [v, i]),
first(([v, i]) => predicate(v, i)),
map(([v, i]) => resultSelector(v, i)),
)
持续()
- 与 resultSelector(v5.x)
source.pipe(
last(predicate, resultSelector, defaultValue)
)
- 没有 resultSelector(如果您不使用其中的索引):
source.pipe(
last(predicate, defaultValue),
map(resultSelector)
)
- 没有 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 参数是可选的,为完整起见在此显示。
source.pipe(
mergeMap(fn1, fn2, concurrency)
)
- 内部地图实现了没有 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)
)
- 没有 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)
)
- 内部地图实现了没有 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)
)
- 没有 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))
)
)
- 内部地图实现了没有 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)
)
- 没有 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)
)
- 内部地图实现了没有 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)
- 没有 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)
- 没有 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)
- 没有 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)
- 没有 resultSelector
fromEvent(button, 'click').pipe(
map(resultSelector)
)
UMD 模块名称更改
在 RxJS v6.x 中,UMD 模块名称已从Rx更改为 rxjs,以便与其他导入模块名称对齐。
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