45、前端开发中的包管理与 RxJS 基础

前端开发中的包管理与 RxJS 基础

1. 包管理基础

在前端开发中,包管理是一个重要的环节,它涉及到项目依赖的安装、版本管理等方面。

  • 依赖分类 :在项目的 package.json 文件中,有两个重要的依赖部分。 dependencies 部分列出了应用运行所需的所有包。例如,Angular 框架有多个以 @angular 开头的包,但并非所有包都需要安装。如果应用不使用表单,就无需在 package.json 中包含 @angular/forms 。而 devDependencies 部分则列出了开发者计算机上需要安装的所有包,这些包在生产服务器上通常是不需要的,比如测试框架和 TypeScript 编译器。
  • npm 安装包 :使用 npm 安装单个包时,只需列出包的名称。例如,要将 Lodash 库添加到 node_modules 目录,可以运行以下命令:
npm i lodash

若要将包添加到 node_modules 并将相应的依赖添加到 package.json 文件的 dependencies 部分,可以明确指定 --save-prod 选项:

npm i lodash --save-prod

也可以使用缩写 -P 来替代 --save-prod 。如果未指定任何选项, npm i lodash 命令会更新 package.json 文件的 dependencies 部分。若要将包添加到 node_modules 并将相应的依赖添加到 package.json 文件的 devDependencies 部分,则使用 --save-dev 选项:

npm i protractor --save-dev

同样,也可以使用缩写 -D 来替代 --save-dev
- 从 GitHub 安装包 :有时,包的 GitHub 版本可能有重要的 bug 修复,但尚未在 npmjs.org 上发布。如果要从 GitHub 安装这样的包,需要将 package.json 依赖中的版本号替换为其在 GitHub 上的位置。例如:

"@angular/flex-layout": "angular/flex-layout-builds"

前提是 Flex Layout 库的主分支没有阻止 npm 安装的代码问题。

2. 语义化版本控制

Angular 版本号采用了一套称为语义化版本控制的规则。一个包的版本由三个数字组成,例如 6.1.2
| 数字位置 | 含义 |
| ---- | ---- |
| 第一个数字 | 表示主要版本,包含新功能,可能会有 API 的重大更改 |
| 第二个数字 | 表示次要版本,引入新的向后兼容的 API,但没有重大更改 |
| 第三个数字 | 表示向后兼容的补丁,用于修复 bug |

package.json 文件中,每个包都有一个三位版本号,并且很多包还有额外的符号: ^ ~
- 如果指定的版本只有三个数字,意味着指示 npm 精确安装该版本。例如:

"@angular/cli": "6.0.5"

这会让 npm 安装 Angular CLI 版本 6.0.5,即使有更新的版本也会忽略。
- 版本号前有 ^ 符号,表示允许 npm 安装该版本的最新次要版本。例如:

"@angular/core": "^6.0.0"

如果 Angular Core 包的最新版本是 6.2.1,它将被安装。
- ~ 符号表示要安装给定主要和次要版本的最新补丁。例如:

"jasmine-core": "~2.99.1"
3. Yarn 作为 npm 的替代方案

Yarn(https://yarnpkg.com)是另一个可以替代 npm 的包管理器。在 npm 版本 5 之前,npm 速度较慢,这是开始使用更快的 Yarn 的一个原因。现在 npm 也很快了,但 Yarn 还有一个额外的好处:它会创建 yarn.lock 文件,用于跟踪项目中安装的包的精确版本。

例如, package.json 文件中有 "@angular/core": "^6.0.0" 依赖,且项目中没有 yarn.lock 文件。如果版本 6.1.0 可用,它将被安装,并创建 yarn.lock 文件记录版本 6.1.0。如果一个月后运行 yarn install ,且项目中存在 yarn.lock 文件,Yarn 将使用该文件并安装版本 6.1.0,即使版本 6.2.0 可用。以下是 yarn.lock 文件的一个片段:

"@angular/core@^6.0.0":
  version "6.0.2"
  resolved "https://registry.yarnpkg.com/@angular/core/-/core-6.0.2.tgz#d183..."
  dependencies:
    tslib "^1.9.0"

在团队开发中,应该将 yarn.lock 文件提交到版本控制仓库,这样团队的每个成员都能使用相同版本的包。虽然 npm 也会创建 package-lock.json 文件,但在运行 npm install 时,npm 不会精确安装该文件中列出的包版本。不过从版本 5.7 开始,npm 支持 npm ci 命令,该命令会忽略 package.json 中列出的版本,而是安装 package-lock.json 文件中列出的版本。

如果想在安装新生成项目的依赖时让 Angular CLI 使用 Yarn 而不是 npm,从 Angular CLI 6 开始,可以使用以下命令:

ng config --global cli.packageManager yarn

如果使用较旧版本的 Angular CLI,则使用以下命令:

ng set --global packageManager=yarn
4. RxJS 基础

在编程中,同步编程相对简单,代码逐行执行。而异步编程会显著增加代码的复杂性。RxJS 6 库可以用于任何基于 JavaScript 的应用,在编写和组合异步代码方面表现出色。

  • 反应式编程概念 :通过一个简单的例子来理解反应式编程。以下代码:
let a1 = 2;
let b1 = 4;
let c1 = a1 + b1;
// c1 = 6

当修改 a1 b1 的值时:

a1 = 55;
// c1 = 6 but should be 59
b1 = 20;
// c1 = 6 but should be 75

c1 的值并不会随着 a1 b1 的变化而自动更新。而在反应式编程中,希望 c2 能在 a1 b1 发生任何变化时自动重新计算。就像在电子表格程序(如 Microsoft Excel)中,在 C1 单元格中放入公式 =sum(a1, b1) ,C1 会在 A1 和 B1 发生变化时立即做出反应。在反应式编程中,数据的变化驱动代码的执行,它是关于创建响应式、事件驱动的应用程序,其中可观察的事件流被推送给订阅者,订阅者观察并处理这些事件。
- RxJS 术语
- Observable :数据随时间推送的流。它是一个函数(或对象),获取生产者的数据并将其推送给订阅者。
- Observer :可观察流的消费者,是一个知道如何处理可观察对象推送的数据元素的对象(或函数)。
- Subscriber :将观察者与可观察对象连接起来。
- Operator :用于途中数据转换的函数。

以下是数据从可观察对象到观察者的流程图:

graph LR
    A[Data provider] --> B[Observable]
    B --> C1[Observer - Subscriber1]
    B --> C2[Observer - Subscriber2]
    B --> C3[Observer - Subscriber3]
    B -- Push --> C1
    B -- Push --> C2
    B -- Push --> C3
5. 冷热可观察对象

可观察对象分为热可观察对象和冷可观察对象。
- 冷可观察对象 :为每个订阅者创建一个数据生产者。例如,在 Netflix 上观看电影,无论何时点击播放按钮,都会为你创建一个新的电影流生产者,你会得到整部电影。冷可观察对象在代码调用 subscribe() 函数时开始产生数据。比如应用声明一个可观察对象,提供服务器上获取特定产品的 URL,只有在订阅时才会发出请求。如果另一个脚本向服务器发出相同的请求,会得到相同的数据集。
- 热可观察对象 :先创建一个数据生产者,每个订阅者从订阅时刻开始从这个生产者获取数据。例如,去电影院看电影,电影在下午 4 点开始放映,如果有人迟到,就会错过电影开头,只能从到达时刻开始观看。热可观察对象即使没有订阅者对数据感兴趣,也会产生数据。比如智能手机中的加速度计会产生设备位置数据,即使没有应用订阅该数据;服务器可以产生最新的股票价格,即使没有用户对该股票感兴趣。

6. 可观察对象、观察者和订阅者

可观察对象从数据源(如套接字、数组、UI 事件)一次获取一个数据元素。它知道如何做三件事:
- 向观察者发出下一个元素。
- 向观察者抛出错误。
- 通知观察者流结束。

相应地,观察者对象提供最多三个回调:
- 处理可观察对象发出的下一个元素的函数。
- 处理可观察对象抛出的错误的函数。
- 处理流结束的函数。

订阅者通过调用 subscribe() 方法连接可观察对象和观察者,并通过调用 unsubscribe() 方法断开它们的连接。例如:

let mySubscription = someObservable.subscribe(myObserver);
mySubscription.unsubscribe();

可观察对象通过在观察者对象上调用以下函数与观察者进行通信:
- next() :将下一个数据元素推送给观察者。
- error() :将错误消息推送给观察者。
- complete() :向观察者发送流结束的信号。

7. 创建可观察对象

RxJS 提供了多种创建可观察对象的方法,具体取决于数据生产者的类型。
| 创建方法 | 描述 |
| ---- | ---- |
| of(1,2,3) | 将数字序列转换为可观察对象 |
| Observable.create(myObserver) | 返回一个可在你创建并作为参数提供的 myObserver 上调用方法的可观察对象 |
| from(myArray) | 将 myArray 变量表示的数组转换为可观察对象,也可以使用任何可迭代的数据集合或生成器函数作为参数 |
| fromEvent(myInput, ‘keyup’) | 将 myInput 表示的 HTML 元素的 keyup 事件转换为可观察对象 |
| interval(1000) | 每秒发出一个连续的整数(0,1,2,3…) |

以下是创建一个发出 1、2 和 3 的可观察对象并订阅它的示例:

of(1,2,3)
.subscribe(
  value => console.log(value),
  err => console.error(err),
  () => console.log("Streaming is over")
);

这里传递给 subscribe() 的三个箭头函数组合起来就是观察者的实现。运行这段代码会在控制台输出:

1
2
3
Streaming is over
8. RxJS 操作符

当数据元素从可观察对象流向观察者时,可以应用一个或多个操作符,这些操作符是在将元素提供给观察者之前处理每个元素的函数。每个操作符以一个可观察对象作为输入,执行其操作,并返回一个新的可观察对象作为输出。

RxJS 大约有 100 个不同的操作符,其文档通常会用弹珠图来展示操作符。例如, map 操作符接受一个转换函数作为参数,并将其应用于每个传入的元素。以下是 map 操作符的弹珠图示例:

1 2 3
   map(x => 10 * x)
10 20 30

filter 操作符接受一个函数谓词作为参数,如果发出的值满足条件则返回 true ,否则返回 false 。只有满足条件的值才会到达订阅者。以下是 filter 操作符的弹珠图示例:

1 0 4 2 3
   filter(x => x % 2 === 1)
1 3

操作符可以链式调用,这样每个可观察元素在被传递给观察者之前可以经过多次转换。

前端开发中的包管理与 RxJS 基础

9. 操作符的链式调用示例

为了更好地理解操作符的链式调用,我们来看一个具体的例子。假设我们有一个可观察对象发出一系列数字,我们想要先过滤掉偶数,然后将剩下的奇数乘以 10。可以使用 filter map 操作符来实现这个需求。

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

of(1, 2, 3, 4, 5)
  .pipe(
    filter(x => x % 2 === 1),
    map(x => x * 10)
  )
  .subscribe(
    value => console.log(value),
    err => console.error(err),
    () => console.log("Streaming is over")
  );

在这个例子中, of(1, 2, 3, 4, 5) 创建了一个发出数字 1 到 5 的可观察对象。 pipe 方法用于将多个操作符连接起来。首先, filter(x => x % 2 === 1) 过滤掉偶数,只保留奇数。然后, map(x => x * 10) 将剩下的奇数乘以 10。最后,订阅这个可观察对象,将处理后的值输出到控制台。运行这段代码,输出结果如下:

10
30
50
Streaming is over
10. 错误处理与完成信号

在 RxJS 中,可观察对象可以发出错误信号,并且在流结束时发出完成信号。我们可以在订阅时提供相应的回调函数来处理这些情况。

import { of } from 'rxjs';

const source = of(1, 2, 3);

source.subscribe(
  value => console.log(value),
  err => console.error('Error:', err),
  () => console.log('Stream completed')
);

在这个例子中, of(1, 2, 3) 创建了一个发出数字 1 到 3 的可观察对象。订阅时,第一个回调函数处理正常发出的值,第二个回调函数处理错误,第三个回调函数在流结束时被调用。由于这个可观察对象不会发出错误,所以不会触发错误回调。运行这段代码,输出结果如下:

1
2
3
Stream completed
11. 订阅的取消

在某些情况下,我们可能需要取消订阅,以避免不必要的资源消耗。可以通过调用 unsubscribe() 方法来取消订阅。

import { interval } from 'rxjs';

const source = interval(1000);

const subscription = source.subscribe(
  value => console.log(value)
);

// 5 秒后取消订阅
setTimeout(() => {
  subscription.unsubscribe();
  console.log('Subscription cancelled');
}, 5000);

在这个例子中, interval(1000) 创建了一个每秒发出一个递增整数的可观察对象。订阅这个可观察对象后,每秒会在控制台输出一个数字。使用 setTimeout 函数在 5 秒后调用 unsubscribe() 方法取消订阅。运行这段代码,会看到前 5 秒每秒输出一个数字,5 秒后输出 Subscription cancelled

12. 结合实际应用场景

在实际的前端开发中,RxJS 可以用于处理各种异步操作,比如处理用户输入、发起 HTTP 请求等。以下是一个处理用户输入的示例:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>RxJS User Input Example</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.6.7/rxjs.umd.min.js"></script>
</head>

<body>
  <input type="text" id="input">
  <script>
    const input = document.getElementById('input');
    const { fromEvent } = rxjs;
    const { map } = rxjs.operators;

    const input$ = fromEvent(input, 'keyup')
     .pipe(
        map(event => event.target.value)
      );

    const subscription = input$.subscribe(
      value => console.log('User input:', value),
      err => console.error('Error:', err),
      () => console.log('Stream completed')
    );
  </script>
</body>

</html>

在这个示例中,使用 fromEvent 操作符将 input 元素的 keyup 事件转换为可观察对象。然后使用 map 操作符提取输入框的值。订阅这个可观察对象后,每当用户在输入框中输入内容并松开按键时,会在控制台输出输入的值。

13. 总结

通过以上内容,我们了解了前端开发中的包管理,包括 npm 和 Yarn 的使用,以及语义化版本控制的规则。同时,深入学习了 RxJS 的基础知识,包括可观察对象、观察者、订阅者、操作符等概念,以及冷热可观察对象的区别。还通过实际的代码示例展示了如何创建可观察对象、使用操作符进行数据处理、处理错误和完成信号、取消订阅等操作。在实际开发中,合理运用这些知识可以帮助我们更好地处理异步操作,提高代码的可维护性和性能。

以下是一个总结的表格,对比了 npm 和 Yarn 的特点:
| 特点 | npm | Yarn |
| ---- | ---- | ---- |
| 速度 | 早期较慢,现在速度提升 | 一直较快 |
| 版本锁定 | 有 package-lock.json ,但 npm install 不精确安装 | 有 yarn.lock ,精确安装记录版本 |
| 团队协作 | 从 5.7 版本开始支持 npm ci 精确安装 | 建议提交 yarn.lock 保证团队版本一致 |

最后,用一个 mermaid 流程图来展示 RxJS 中数据从可观察对象到观察者的处理流程:

graph LR
    A[Data Source] --> B[Observable]
    B --> C{Operator 1}
    C --> D{Operator 2}
    D --> E[Observer]
    B -- Push --> C
    C -- Process --> D
    D -- Push --> E

这个流程图展示了数据从数据源到可观察对象,经过多个操作符处理后到达观察者的过程。每个操作符对数据进行特定的处理,最终将处理后的数据推送给观察者。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值