• 理解操作符导入
    • 简单示例
    • 解决方案
    • Pipeable 操作符

    理解操作符导入

    在消费或创建依赖于 RxJS 的公共库时,你可能遇到处理运算符导入的问题。在项目中引入操作符最主要的方式像下面这样导入:

    1. import 'rxjs/add/operator/take';

    这会将导入的操作符添加到 Observable 的原型上,以便在整个项目中使用:

    (源码)

    1. import { Observable } from '../../Observable';
    2. import { take } from '../../operator/take';
    3. Observable.prototype.take = take;
    4. declare module '../../Observable' {
    5. interface Observable<T> {
    6. take: typeof take;
    7. }
    8. }

    对于私有项目和模块,这种方法通常是“没问题的”,但当整个团队使用同一个 npm 包或库时再使用这种导入方式,问题就会出现。

    简单示例

    来看看问题出在哪里,假设小A创建了一个公有的 Angular 组件库。在这个库中需要一些操作符,下面以传统的方式进行了导入:

    some-public-library.ts

    1. import 'rxjs/add/operator/take';
    2. import 'rxjs/add/operator/concatMap';
    3. import 'rxjs/add/operator/switchMap';

    小B引用了小A的库。尽管他并没有导入这些操作符,但他依然可以直接使用。这可能不是什么大问题,但确实会带来一些困扰。小B继续使用库和这些操作符,一切都很正常…

    一个月后,小A决定更新自己的库。他不再需要 switchMapconcatMap,所以他删除了导入:

    some-public-library.ts

    1. import 'rxjs/add/operator/take';

    小B更新了依赖并构建自己的项目,但这次失败了。他本身并没有引入 switchMapconcatMap,只是基于第三方的依赖才能正常运行。如果你并没有意识到这样会产生问题,那可能需要一点时间来弄清楚。

    解决方案

    不再使用这种导入方式:

    1. import 'rxjs/add/operator/take';

    我们可以这样来进行导入:

    1. import { take } from 'rxjs/operator/take';

    这样可以保持 Observable 原型的干净,这样来直接调用它们:

    1. import { take } from 'rxjs/operator/take';
    2. import { of } from 'rxjs/observable/of';
    3. take.call(
    4. of(1,2,3),
    5. 2
    6. );

    然而这样代码很快就会变得丑陋不堪,想象一下这样更长的调用链:

    1. import { take } from 'rxjs/operator/take';
    2. import { map } from 'rxjs/operator/map';
    3. import { of } from 'rxjs/observable/of';
    4. map.call(
    5. take.call(
    6. of(1,2,3),
    7. 2
    8. ),
    9. val => val + 2
    10. );

    很快我们的代码块将变得几乎无法理解。我们怎样才能两全其美呢?

    Pipeable 操作符

    现在 RxJS 提供了 pipe 辅助函数,它存在于 Observable 上,它缓解了操作符不在原型上所带来的问题。我们还继续使用上面丑陋的代码块:

    1. import { take, map } from 'rxjs/operators';
    2. import { of } from 'rxjs/observable/of';
    3. map.call(
    4. take.call(
    5. of(1,2,3),
    6. 2
    7. ),
    8. val => val + 2
    9. );

    并将其转换成:

    1. import { take, map } from 'rxjs/operators';
    2. import { of } from 'rxjs/observable/of';
    3. of(1,2,3)
    4. .pipe(
    5. take(2),
    6. map(val => val + 2)
    7. );

    Much easier to read, right? This also has the benefit of greatly reducing the RxJS bundle size in your application. For more on this, check out Ashwin Sureshkumar’s excellent article Reduce Angular app bundle size using lettable operators.

    代码可读性更高了,是吧?它还有额外的好处,就是可以大大减少应用中 RxJS 的打包尺寸。想深入了解,请参见 Ashwin Sureshkumar’s 的精彩文章 使用 lettable 操作符来减少 Angular 应用的打包尺寸。