前端开发中的包管理与 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
这个流程图展示了数据从数据源到可观察对象,经过多个操作符处理后到达观察者的过程。每个操作符对数据进行特定的处理,最终将处理后的数据推送给观察者。
超级会员免费看

68

被折叠的 条评论
为什么被折叠?



