Skip to content

Commit 780cd33

Browse files
authored
docs(rxjs): rxjs usage and practice (#64)
1 parent 109cf5d commit 780cd33

34 files changed

+6053
-989
lines changed

content/docs/framework/_index.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
---
22
title: Framework
33
weight: 2
4-
draft: true
5-
---
4+
---

content/docs/framework/angular/01_env.md

-21
This file was deleted.

content/docs/framework/angular/03_router.md

+21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# 路由
22

3+
## 路由器事件
4+
5+
在每次导航过程中,路由器都会通过 `Router.events` 属性发出导航事件。这些事件如下表所示。
6+
7+
- [NavigationStart](https://angular.dev/api/router/NavigationStart) :导航开始。
8+
- [RouteConfigLoadStart](https://angular.dev/api/router/RouteConfigLoadStart) :在路由器 lazy loads 之前进行路由配置。
9+
- [RouteConfigLoadEnd](https://angular.dev/api/router/RouteConfigLoadEnd) :延迟加载路由后。
10+
- [RoutesRecognized](https://angular.dev/api/router/RoutesRecognized) :当路由器解析 URL 并识别路由时。
11+
- [GuardsCheckStart](https://angular.dev/api/router/GuardsCheckStart) :当路由器开始路由的保护阶段时。
12+
- [ChildActivationStart](https://angular.dev/api/router/ChildActivationStart) :当路由器开始激活路由的子级时。
13+
- [ActivationStart](https://angular.dev/api/router/ActivationStart) :当路由器开始激活路由时。
14+
- [GuardsCheckEnd](https://angular.dev/api/router/GuardsCheckEnd) :当路由器成功完成路由的保护阶段时。
15+
- [ResolveStart](https://angular.dev/api/router/ResolveStart) :当路由器开始路由的解析阶段时。
16+
- [ResolveEnd](https://angular.dev/api/router/ResolveEnd) :当路由器成功完成路由的解析阶段时。
17+
- [ChildActivationEnd](https://angular.dev/api/router/ChildActivationEnd) :当路由器完成激活路由的子级时。
18+
- [ActivationEnd](https://angular.dev/api/router/ActivationEnd) :当路由器完成激活路由时。
19+
- [NavigationEnd](https://angular.dev/api/router/NavigationEnd) :当导航成功结束时。
20+
- [NavigationCancel](https://angular.dev/api/router/NavigationCancel) :当取消导航时。
21+
- [NavigationError](https://angular.dev/api/router/NavigationError) :当导航由于意外错误而失败时。
22+
- [Scroll](https://angular.dev/api/router/Scroll) :当用户滚动时。
23+
324
## 路由守卫
425

526
有时候需要控制对该应用的不同路由的访问。可能包括如下场景:

content/docs/framework/angular/08_other.md

+5-7
Original file line numberDiff line numberDiff line change
@@ -275,13 +275,12 @@ export class SampleComponent implements AfterViewInit {
275275

276276
Angular 支持两种 View:
277277

278-
Embedded View,指 `Template`
279-
Host View,指 `Component`
278+
- Embedded View,指 `Template`
279+
- Host View,指 `Component`
280280

281281
### ViewContainerRef
282282

283-
`ViewContainerRef` 是可以容纳一个或者多个 View 的容器。任何 DOM 元素都可以作为视图容器,然而对于绑定 `ViewContainer` 的 DOM 元素,Angular 不会
284-
把视图插入该元素的内部,而是追加到该元素后面,这类似于 `router-outlet` 中插入组件的方式。
283+
`ViewContainerRef` 是可以容纳一个或者多个 View 的容器。任何 DOM 元素都可以作为视图容器,然而对于绑定 `ViewContainer` 的 DOM 元素, Angular 不会把视图插入该元素的内部,而是追加到该元素后面,这类似于 `router-outlet` 中插入组件的方式。
285284

286285
通常,把 `ViewContainer` 绑定在 `ng-container` 元素上,因为 `ng-container` 元素会被渲染为注释,从而不会在 DOM 中引入多余的 html 元素。
287286

@@ -304,8 +303,7 @@ export class SampleComponent implements AfterViewInit {
304303
}
305304
```
306305

307-
和其他 DOM 抽象类一样,`ViewContainer` 绑定到特殊的 DOM 元素,并可以通过 `element` 访问到。例如上例中,它绑定到` ng-container` 元素上,
308-
并且渲染为 HTML 注释,所以输出会是 `template bindings={}`
306+
和其他 DOM 抽象类一样,`ViewContainer` 绑定到特殊的 DOM 元素,并可以通过 `element` 访问到。例如上例中,它绑定到` ng-container` 元素上,并且渲染为 HTML 注释,所以输出会是 `template bindings={}`
309307

310308
`ViewContainer` 提供了一些操作视图 API:
311309

@@ -396,7 +394,7 @@ export class SampleComponent {}
396394

397395
### Renderer2
398396

399-
上面的代码,设置 div 元素的背景,是默认的运行环境在是浏览器。我们要尽量减少应用层与渲染层之间强耦合关系,从而让我们应用能够灵活地运行在不同环境。
397+
上面的代码,设置 `div` 元素的背景,是默认的运行环境在是浏览器。我们要尽量减少应用层与渲染层之间强耦合关系,从而让我们应用能够灵活地运行在不同环境。
400398

401399
为了能够支持跨平台,Angular 通过抽象层封装了不同平台的差异。比如定义了抽象类 `Renderer2` 、抽象类 `RootRenderer` 等。此外还定义了以下引用
402400
类型:`ElementRef``TemplateRef``ViewRef``ComponentRef``ViewContainerRef` 等。通过 `模板变量``@ViewChild` 等方法获取 DOM 元素。

content/docs/framework/angular/10_error.md

+153
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,156 @@ export default {
162162
};
163163

164164
```
165+
166+
167+
ExpressionChangedAfterItHasBeenCheckedError 是 Angular 的一个常见错误,它通常出现在开发模式下。当 Angular 检测到某个值在检测周期之后发生了变化时,就会抛出此错误。
168+
169+
错误背景
170+
Angular 采用 变更检测(Change Detection) 的机制,来确保视图(HTML)中的数据和组件中的数据保持同步。每当发生变化时,Angular 会进行变更检测。如果在变更检测过程中,某个数据发生了变化,Angular 会检测到不一致,并抛出 ExpressionChangedAfterItHasBeenCheckedError 错误。
171+
172+
这个错误通常发生在:
173+
174+
在组件的 ngOnInit 或构造函数中修改了绑定的属性(视图绑定)。
175+
在生命周期钩子(如 ngAfterViewInit 或 ngAfterViewChecked)中修改了绑定值。
176+
当你使用了异步数据(例如:setTimeout、Promise、Observable 等)来更新视图。
177+
178+
179+
错误示例
180+
typescript
181+
Copy code
182+
export class MyComponent implements OnInit {
183+
title = 'Initial title';
184+
185+
ngOnInit() {
186+
// 在 ngOnInit 中改变了 title
187+
setTimeout(() => {
188+
this.title = 'Updated title';
189+
}, 0);
190+
}
191+
}
192+
在这个例子中,setTimeout 会在 Angular 完成第一次变更检测后修改 title,导致 Angular 检测到数据不一致,从而抛出错误。
193+
194+
如何解决这个错误
195+
1. 使用 ChangeDetectorRef.detectChanges()
196+
可以显式地触发变更检测,以告诉 Angular 更新视图。
197+
198+
typescript
199+
Copy code
200+
import { Component, ChangeDetectorRef, OnInit } from '@angular/core';
201+
202+
@Component({
203+
selector: 'app-my-component',
204+
templateUrl: './my-component.component.html',
205+
})
206+
export class MyComponent implements OnInit {
207+
title = 'Initial title';
208+
209+
constructor(private cdRef: ChangeDetectorRef) {}
210+
211+
ngOnInit() {
212+
setTimeout(() => {
213+
this.title = 'Updated title';
214+
this.cdRef.detectChanges(); // 手动触发变更检测
215+
}, 0);
216+
}
217+
}
218+
在上面的代码中,detectChanges() 会强制 Angular 重新运行变更检测,从而解决了错误。
219+
220+
2. 使用 ngAfterViewInit 或 ngAfterViewChecked
221+
你可以将属性的更新放在 ngAfterViewInit 或 ngAfterViewChecked 生命周期钩子中。ngAfterViewInit 在视图初始化之后调用,因此不会立即触发错误。
222+
223+
typescript
224+
Copy code
225+
import { Component, AfterViewInit } from '@angular/core';
226+
227+
@Component({
228+
selector: 'app-my-component',
229+
templateUrl: './my-component.component.html',
230+
})
231+
export class MyComponent implements AfterViewInit {
232+
title = 'Initial title';
233+
234+
ngAfterViewInit() {
235+
setTimeout(() => {
236+
this.title = 'Updated title';
237+
}, 0);
238+
}
239+
}
240+
使用 ngAfterViewInit 或 ngAfterViewChecked 可以避免 Angular 在当前变更检测周期中检测到不一致的情况,因为这两个钩子会在视图初始化后调用。
241+
242+
3. 使用 ChangeDetectionStrategy.OnPush
243+
如果你的组件使用了 ChangeDetectionStrategy.OnPush,Angular 会只在输入属性变化时重新运行变更检测。在这种情况下,你需要显式地触发变更检测,通常使用 ChangeDetectorRef。
244+
245+
typescript
246+
Copy code
247+
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnInit } from '@angular/core';
248+
249+
@Component({
250+
selector: 'app-my-component',
251+
templateUrl: './my-component.component.html',
252+
changeDetection: ChangeDetectionStrategy.OnPush,
253+
})
254+
export class MyComponent implements OnInit {
255+
title = 'Initial title';
256+
257+
constructor(private cdRef: ChangeDetectorRef) {}
258+
259+
ngOnInit() {
260+
setTimeout(() => {
261+
this.title = 'Updated title';
262+
this.cdRef.markForCheck(); // 标记组件为需要检查
263+
}, 0);
264+
}
265+
}
266+
在这个例子中,markForCheck() 会告诉 Angular 检查该组件的变更,从而避免在生命周期钩子中直接更改数据。
267+
268+
4. 延迟更新
269+
在某些情况下,你可以通过将数据更新操作延迟到下一个 JavaScript 事件循环中,来避免在变更检测过程中更改值。
270+
271+
typescript
272+
Copy code
273+
export class MyComponent implements OnInit {
274+
title = 'Initial title';
275+
276+
ngOnInit() {
277+
setTimeout(() => {
278+
this.title = 'Updated title';
279+
}, 0); // 延迟更新到下一个事件循环
280+
}
281+
}
282+
这会将更新放入队列中,确保它在变更检测完成后执行,从而避免错误。
283+
284+
5. 使用 zone.js NgZone.run() (不推荐)
285+
NgZone 可以用来在 Angular 的 Zone 之外执行异步操作,并确保变更检测周期被正确处理。然而,这种方法通常不推荐,因为它可能会绕过 Angular 的正常变更检测机制,可能导致其他问题。
286+
287+
typescript
288+
Copy code
289+
import { Component, NgZone } from '@angular/core';
290+
291+
@Component({
292+
selector: 'app-my-component',
293+
templateUrl: './my-component.component.html',
294+
})
295+
export class MyComponent {
296+
title = 'Initial title';
297+
298+
constructor(private ngZone: NgZone) {}
299+
300+
ngOnInit() {
301+
this.ngZone.run(() => {
302+
setTimeout(() => {
303+
this.title = 'Updated title';
304+
}, 0);
305+
});
306+
}
307+
}
308+
这种方法通常更适用于更复杂的场景,基本情况下推荐使用其他解决方案。
309+
310+
总结
311+
ExpressionChangedAfterItHasBeenCheckedError 是因为在 Angular 的变更检测周期内,某个属性的值发生了变化。常见的解决方案有:
312+
313+
使用 ChangeDetectorRef.detectChanges() 来手动触发变更检测。
314+
使用 ngAfterViewInit 或 ngAfterViewChecked 来延迟数据的更新。
315+
延迟数据更新到下一个事件循环(例如,使用 setTimeout())。
316+
如果使用 OnPush 策略,确保显式触发变更检测。
317+
通常情况下,推荐使用 ChangeDetectorRef.detectChanges() 或 markForCheck() 来解决此问题。

0 commit comments

Comments
 (0)