【问题标题】:Importing lettable RxJS operators导入 lettable RxJS 操作符
【发布时间】:2018-04-01 21:48:08
【问题描述】:

RxJS 5.5 做了一个重大的改变并引入了lettable operators 来替换我们以前使用的基本上所有的操作符(称为“补丁”操作符)。

那篇文章包含一个注释:

Lettable 运算符现在可以从 rxjs/operators 导入,但是这样做 因此,在不改变构建过程的情况下,通常会导致更大的 应用程序包。这是因为默认情况下 rxjs/operators 会 解析为 rxjs 的 CommonJS 输出。

这个声明很容易证明使用全新的 AngularCLI 生成的应用程序的实践。

当我们有一个不从 RxJS 导入任何东西的应用程序时:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from "@angular/common/http";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  public title = 'app';

  constructor(private readonly http: HttpClient) {
  }

  public ngOnInit(): void {
    this.http.get('https://api.github.com/users')
      .subscribe(response => {
        console.log(response);
      });
  }
}

我们可以看到以下内容:

ng build --prod
chunk {0} polyfills.e1f97a0070e18e96a6be.bundle.js (polyfills) 61.4 kB {4} [initial] [rendered]
chunk {1} main.b2b5d212102ca9d103e8.bundle.js (main) 4.92 kB {3} [initial] [rendered]
chunk {2} styles.d41d8cd98f00b204e980.bundle.css (styles) 0 bytes {4} [initial] [rendered]
chunk {3} vendor.4b7be3dbe842aec3f0ab.bundle.js (vendor) 236 kB [initial] [rendered]
chunk {4} inline.387c7023e5627ac04221.bundle.js (inline) 1.45 kB [entry] [rendered]

当我们以“旧”方式导入 RxJS 运算符并使用它时:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import "rxjs/add/operator/map";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  public title = 'app';

  constructor(private readonly http: HttpClient) {
  }

  public ngOnInit(): void {
    this.http.get('https://api.github.com/users')
      .map((u: any) => 1)
      .subscribe(response => {
        console.log(response);
      });
  }
}

我们可以看到捆绑包的大小没有增加:

chunk {0} polyfills.e1f97a0070e18e96a6be.bundle.js (polyfills) 61.4 kB {4} [initial] [rendered]
chunk {1} main.229ad10195bbb426b3e8.bundle.js (main) 4.96 kB {3} [initial] [rendered]
chunk {2} styles.d41d8cd98f00b204e980.bundle.css (styles) 0 bytes {4} [initial] [rendered]
chunk {3} vendor.933334fc50e7008778fe.bundle.js (vendor) 236 kB [initial] [rendered]
chunk {4} inline.6a52179d8b19cd3cc179.bundle.js (inline) 1.45 kB [entry] [rendered]

当我们尝试按照建议导入和使用 lettable 运算符但不修改构建过程时:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { map } from "rxjs/operators";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  public title = 'app';

  constructor(private readonly http: HttpClient) {
  }

  public ngOnInit(): void {
    this.http.get('https://api.github.com/users').pipe(
        map((u: any) => 1))
      .subscribe(response => {
        console.log(response);
      });
  }
}

我们看到供应商捆绑包大了 108 kB,这告诉我们 RxJS 没有经过 tree-shaking:

chunk {0} polyfills.e1f97a0070e18e96a6be.bundle.js (polyfills) 61.4 kB {4} [initial] [rendered]
chunk {1} main.450c741a106157402dcd.bundle.js (main) 4.97 kB {3} [initial] [rendered]
chunk {2} styles.d41d8cd98f00b204e980.bundle.css (styles) 0 bytes {4} [initial] [rendered]
chunk {3} vendor.3f53f0e2283f4c44ec38.bundle.js (vendor) 344 kB [initial] [rendered]
chunk {4} inline.2d973ef5a10aa806b082.bundle.js (inline) 1.45 kB [entry] [rendered]

当我尝试按照文章No Control over Build Process 部分中的建议导入 lettable 运算符时:

import { map } from "rxjs/operators/map";

我收到构建错误:

./src/app/app.component.ts
Module not found: Error: Can't resolve 'rxjs/operators/map' in 'c:\Projects\Angular\src\app'
 @ ./src/app/app.component.ts 14:0-41
 @ ./src/app/app.module.ts
 @ ./src/main.ts
 @ multi webpack-dev-server/client?http://localhost:4200 ./src/main.ts
  1. 我做错了什么?
  2. 我们如何在 Angular CLI 应用程序中导入新的 RxJS lettable 运算符 这样 RxJS 仍然会被摇树?

更新: 软件包版本(目前基本上都是 AngularCLI 应用程序的最新“想要”版本):

rxjs: 5.5.0
@angular/cli: 1.4.9
node: 8.6.0
os: win32 x64
@angular/animations: 4.4.6
@angular/common: 4.4.6
@angular/compiler: 4.4.6
@angular/core: 4.4.6
@angular/forms: 4.4.6
@angular/http: 4.4.6
@angular/platform-browser: 4.4.6
@angular/platform-browser-dynamic: 4.4.6
@angular/router: 4.4.6
@angular/cli: 1.4.9
@angular/compiler-cli: 4.4.6
@angular/language-service: 4.4.6
typescript: 2.3.4

【问题讨论】:

  • 这可能会非常令人惊讶,请参阅github.com/webpack/webpack/issues/2867
  • @kemsky:感谢您提供的信息!我提供的文章中解释了它不摇树的原因。问题基本上是如何使用当前的 AngularCLI 构建系统来解决它?
  • 能否请您发布ng version 输出。我发现@angular/cli: 1.2.0, @angular/___: 4.4.6, rxjs@5.5.0-beta.7 没有构建错误。
  • @RichardMatsen:当然,我已经更新了我的问题。如果你能用rxjs: 5.5.0(不是beta)和@angular/cli: 1.4.9试试的话,我会非常感谢你。
  • 我更新到@angular/cli: 1.4.9 出现了问题。

标签: javascript angular typescript rxjs reactive-programming


【解决方案1】:

为了完整起见,@angular/cli@1.4.9 在 models/webpack-configs/common.js

中添加了以下内容
// Read the tsconfig to determine if we should prefer ES2015 modules.
// Load rxjs path aliases.
// https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#build-and-treeshaking
let alias = {};
try {
    const rxjsPathMappingImport = 'rxjs/_esm5/path-mapping';
    const rxPaths = require_project_module_1.requireProjectModule(projectRoot, rxjsPathMappingImport);
    alias = rxPaths(nodeModules);
}
catch (e) { }

    resolve: {
        extensions: ['.ts', '.js'],
        modules: ['node_modules', nodeModules],
        symlinks: !buildOptions.preserveSymlinks,
        alias
    },

rxjs/_esm5/path-mapping.js有这两个条目

"rxjs/operators": path.resolve(PATH_REPLACEMENT, "rxjs/_esm5/operators/index.js"),
...
"rxjs/operators/map": path.resolve(PATH_REPLACEMENT, "rxjs/_esm5/operators/map.js"),

错误信息的关键是

aliased with mapping 'rxjs/operators': 
  'C:\Dev\Projects\rx55\node_modules\rxjs\_esm5\operators\index.js' to 
  'C:\Dev\Projects\rx55\node_modules\rxjs\_esm5\operators\index.js/map'
...
C:\Dev\Projects\rx55\node_modules\rxjs\_esm5\operators\index.js\map doesn't exist

所以第一个映射会干扰第二个映射。

通过颠倒构建工作的映射顺序,所以在我看来,问题出在 rxjs v5.5。

也就是说,Alexander 的解决方法是解决问题之前的路径。

【讨论】:

    【解决方案2】:
    1. 确保您至少确实在使用5.5.0。否则检查文件node_modules/rxjs/operators/map.js 是否存在,因为我不知道它怎么会不存在。同样使用import { map } from "rxjs/operators"; 会在下面导入相同的文件,所以我怀疑您的构建系统有问题。

    2. 使用运算符的正确方法是从rxjs/operators/* 导入它们(例如,就像您对import { map } from "rxjs/operators/map"; 所做的那样)。

      如果您从rxjs/operators 导入,则与在RxJS rxjs 导入相同,因为您实际上是在导入rxjs/operators/index,请参阅https://github.com/ReactiveX/rxjs/blob/master/src/operators/index.ts

      这就是它没有得到“tree-shaken”的原因,它导入了index.ts中列出的所有运算符。

    【讨论】:

    • 我检查了我的 RxJS 版本,它是 5.5.0。该文件确实存在。在我的问题中,我在全新的 AngularCLI 生成的应用程序上对其进行了测试——您可以自己轻松地检查它。这不是构建系统问题。那篇文章演示了像 import { map } from "rxjs/operators/map"; 这样的深度导入是另一种导入方式,以防您无法修改构建过程以添加 ModuleConcatenationPlugin 插件以对可租用运算符进行 tree-shake。我不确定为什么它无法使用深度导入找到一些东西。也许这与 AngularCLI 构建设置有关?
    【解决方案3】:

    事实证明(感谢@RichardMatsen),这是angular-cli 1.4.9 中的一个错误。

    尝试使用angular-cli 1.4.8 进行深度导入(如import { map } from "rxjs/operators/map";),没有构建错误,并且包大小为:

    chunk {0} polyfills.e1f97a0070e18e96a6be.bundle.js (polyfills) 61.4 kB {4} [initial] [rendered]
    chunk {1} main.d36cf6834f640163ff35.bundle.js (main) 4.97 kB {3} [initial] [rendered]
    chunk {2} styles.d41d8cd98f00b204e980.bundle.css (styles) 0 bytes {4} [initial] [rendered]
    chunk {3} vendor.658e9efd9845db281b29.bundle.js (vendor) 241 kB [initial] [rendered]
    chunk {4} inline.c9d245ca6c859aaeef69.bundle.js (inline) 1.45 kB [entry] [rendered]
    

    与完全不使用 RxJS 运算符的版本相比,仅显示了 5 kB 的增益。

    所以现在,我们至少有一个解决方法:继续使用 angular-cli 1.4.8 并通过深度导入导入 lettable 操作符,例如:

    import { map } from "rxjs/operators/map";
    import { filter } from "rxjs/operators/filter";
    

    【讨论】:

      猜你喜欢
      • 2018-08-14
      • 2018-04-21
      • 1970-01-01
      • 2018-05-13
      • 2018-06-10
      • 1970-01-01
      • 2018-05-01
      • 2016-11-06
      相关资源
      最近更新 更多