angular
什么是Angular
Angular是一个开发平台,它允许开发者使用HTML、CSS和TypeScript创建Web应用程序。它结合了声明性模板、依赖注入、端到端工具和集成最佳实践,以解决开发人员面临的挑战。Angular的设计目标是以Web为中心的平台,以支持在各种不同的客户端上运行的应用程序。Angular是完全开源的,免费的,用于开发Web应用程序的前端框架。它由Google维护,用于创建单页面应用程序(SPA)。
Angular是一个框架,不是类库,提供一整套方案用于设计web应用。它不仅仅是一个框架,因为它的核心其实是对HTML标签的增强。
何为HTML标签增强?其实就是使你能够用标签完成一部分页面逻辑,具体方式就是通过自定义标签、自定义属性等,这些HTML原生没有的标签/属性在ng中有一个名字:指令(directive)。与传统web系统相区别,web应用能为用户提供丰富的操作,能够随用户操作不断更新视图而不进行url跳转。ng官方也声明它更适用于开发CRUD应用,即数据操作比较多的应用,而非是游戏或图像处理类应用。
为了实现这些,ng引入了一些非常棒的特性,包括模板机制、数据绑定、模块、指令、依赖注入、路由。通过数据与模板的绑定,能够让我们摆脱繁琐的DOM操作,而将注意力集中在业务逻辑上。
ng是MVC框架吗?还是MVVM框架?官网有提到ng的设计采用了MVC的基本思想,而又不完全是MVC,因为在书写代码时我们确实是在用ng-controller这个指令(起码从名字上看,是MVC吧),但这个controller处理的业务基本上都是与view进行交互,这么看来又很接近MVVM。让我们把目光移到官网那个非醒目的title上:“AngularJS — Superheroic JavaScript MVW Framework”。
什么时候使用Angular
如果你要开发的是单页应用,AngularJS就是你的上上之选。Gmail、Google Docs、Twitter和Facebook这样的应用,都很能发挥Angular的长处。但是像游戏开发之类对DOM进行大量操纵、又或者单纯需要极高运行速度的应用,就不是Angular的用武之地了。
Angular的特性
特性一: 双向数据绑定
AngularJS最大的特色就是双向数据绑定,这是AngularJS与其他框架最大的不同之处。在AngularJS中,数据模型(Model)的改变会自动反映到视图(View),反之亦然。这种自动同步使得开发者不必手动操作DOM,从而大大提高了开发效率。
1 |
|
特性二:模板
在 Angular 中,模板就是一块 HTML。你可以在模板中通过一种特殊的语法来使用 Angular 的诸多特性。几乎所有的 HTML 语法都是有效的模板语法。但是,由于 Angular 模板只是整个网页的一部分,而不是整个网页,因此不需要包含诸如 ,
或1 | // hello.component.ts |
1 | // hello.component.html |
管道
管道是一种简单的方法,用于格式化输出。它们以 | 作为分隔符,并可以接受任意数量的可选参数来完成任务。管道可以用在模板插值、绑定表达式或者绑定属性中。
模板变量
在模板中,要使用井号 # 来声明一个模板变量。下列模板变量 #phone 声明了一个名为 phone 的变量,其值为此 <input>
元素。
1 | <input #phone placeholder="phone number" /> |
特性三: 组件
组成
- 一个 HTML 模板,用于声明页面要渲染的内容
- 一个用于定义行为的 TypeScript 类
- 一个 CSS 选择器,用于定义组件在模板中的使用方式(指的是
selector: 'app-component'
) - (可选)要应用在模板上的 CSS 样式
生命周期
- ngOnChanges:当 Angular(重新)设置数据绑定输入属性时响应。 该方法接受当前和上一属性值的 SimpleChanges 对象。 当被绑定的输入属性的值发生变化时调用,首次调用一定会发生在 ngOnInit() 之前。如果组件没有输入属性,该方法不会被调用。
- ngOnInit:在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。在第一轮 ngOnChanges() 完成之后调用,只调用一次。
- ngDoCheck:检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。在每个 Angular 变更检测周期中调用,ngOnChanges() 和 ngOnInit() 之后。
- ngAfterContentInit:当把内容投影进组件之后调用。第一次 ngDoCheck() 之后调用,只调用一次。
- ngAfterContentChecked:每次完成被投影组件内容的变更检测之后调用。ngAfterContentInit() 和每次 ngDoCheck() 之后调用。
- ngAfterViewInit:初始化完组件及其子视图之后调用。第一次 ngAfterContentChecked() 之后调用,只调用一次。
- ngAfterViewChecked:每次做完组件视图和子视图的变更检测之后调用。ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。
- ngOnDestroy:当 Angular 每次销毁指令/组件之前调用并清扫。在 Angular 销毁指令/组件之前调用。在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。
视图封装
- ViewEncapsulation.None:Angular 不应用任何形式的视图封装,这意味着为组件指定的任何样式实际上都是全局应用的,并且可以影响应用程序中存在的任何 HTML 元素。这种模式本质上与将样式包含在 HTML 本身中是一样的。
- ViewEncapsulation.ShadowDom:Angular 使用 Shadow DOM 将组件的 CSS 应用于组件的 HTML。Shadow DOM 是浏览器的功能,用于将隐藏的、私有的、独立的 DOM 附加到元素(在这种情况下,组件的元素)上。Shadow DOM 附加到组件元素上的 DOM 是组件的私有 DOM。Shadow DOM 中的样式不会泄漏到组件外部。这意味着,如果你在组件中设置了全局样式,它不会影响应用程序中的其他元素。
- ViewEncapsulation.Emulated(模拟视图):Angular 使用组件的选择器属性为组件元素生成一个属性,然后将该属性附加到每个组件中的元素上。然后,Angular 将组件的样式应用于该属性。这种模式本质上与将样式包含在 HTML 本身中是一样的。但是,它允许你在组件中使用本地 CSS,而不必担心它会影响应用程序中的其他元素。这里生成的属性是类似
_nghost-pmm-5
、_ngcontent-pmm-6
这种。
组件通信
父组件向子组件传值
- @Input():父组件通过属性绑定的方式向子组件传值,子组件通过
@Input()
装饰器来接收父组件传递过来的值。 - get/set | ngOnChanges:父组件通过属性绑定的方式向子组件传值,子组件通过
get/set
或者ngOnChanges
来接收父组件传递过来的值。
子组件向父组件传值
- @Output():子组件通过
@Output()
装饰器来向父组件传值,父组件通过事件绑定的方式来接收子组件传递过来的值。子组件暴露一个EventEmitter
属性,当事件发生时,子组件利用该属性emits
(发出)事件。父组件绑定到这个事件属性,并在事件发生时作出回应。
父级调用@ViewChild()
- @ViewChild():父组件通过
@ViewChild()
装饰器来调用子组件的方法。这个本地变量方法是个简单明了的方法。但是它也有局限性,因为父组件-子组件的连接必须全部在父组件的模板中进行。把子组件的视图插入到父组件类需要做一点额外的工作。首先,必须导入对装饰器 ViewChild 以及生命周期钩子 AfterViewInit 的引用。
父组件和子组件通过服务来通讯 ⭐⭐⭐⭐⭐
- 父组件和它的子组件共享同一个服务,利用该服务在组件家族内部实现双向通讯。该服务实例的作用域被限制在父组件和其子组件内。这个组件子树之外的组件将无法访问该服务或者与它们通讯。
组件样式
:host
: 每个组件都会关联一个与其组件选择器相匹配的元素。这个元素称为宿主元素,模板会渲染到其中。:host 伪类选择器可用于创建针对宿主元素自身的样式,而不是针对宿主内部的那些元素。:host 选择是是把宿主元素作为目标的唯一方式。除此之外,你将没办法指定它,因为宿主不是组件自身模板的一部分,而是父组件模板的一部分。
内容投影
单插槽内容投影
<ng-content></ng-content>
:在父组件中使用<ng-content></ng-content>
标签来接收子组件传递过来的内容。
1 | // 父组件 |
1 | // 子组件 |
多插槽内容投影
- 通过使用
ng-content
的 select 属性来决定将哪些内容放入插槽
1 | // 父组件 |
1 | // 子组件 |
有条件的内容投影
如果你的组件需要有条件地渲染内容或多次渲染内容,则应配置该组件以接受一个
1 | // zippy.template.html |
1 | // zippy.component.ts |
1 | // app.component.html |
Angular元素
- Angular元素就是打包成自定义元素的 Angular 组件
特性四:模块
模块的作用
NgModule 是一个带有 @NgModule 装饰器的类。@NgModule 的参数是一个元数据对象,用于描述如何编译组件的模板,以及如何在运行时创建注入器。它会标出该模块自己的组件、指令和管道,通过 exports 属性公开其中的一部分,以便外部组件使用它们。
JS模块与NgModule的区别
- JS模块是为了解决JS文件之间的依赖关系,JavaScript 模块是一个带有 JavaScript 代码的单独文件,它通常包含一个应用中特定用途的类或函数库。JavaScript 模块让你可以跨多个文件进行工作。
- 而NgModule是为了解决组件之间的依赖关系,它是带有供编译用的元数据的类。
NgModule组成
- declarations:告诉 Angular 哪些组件属于该模块。declarations 数组只能接受可声明对象。可声明对象包括组件、指令和管道。一个模块的所有可声明对象都必须放在 declarations 数组中。可声明对象必须只能属于一个模块,如果同一个类被声明在了多个模块中,编译器就会报错。
- imports: 告诉 Angular 该模块需要哪些模块。imports 数组只能接受 NgModule 类。一个模块可以导入多个模块,也可以不导入任何模块。
- providers: 告诉 Angular 该模块提供了哪些服务。providers 数组只能接受服务类。服务类是一个具有 @Injectable() 装饰器的类,它用于提供应用所需的服务。服务类可以在任何地方使用,包括组件、指令、管道或另一个服务类中。
- bootstrap: 告诉 Angular 该模块的主组件是什么。bootstrap 数组只能接受组件类。一个模块只能有一个主组件,如果有多个主组件,编译器就会报错。
特性模块与根模块
- 特性模块:特性模块是一个带有 @NgModule 装饰器的类,它也是一个 NgModule 类。特性模块的元数据与根模块的元数据类似,但也有一些重要的区别。特性模块的元数据中没有 bootstrap 属性,因为特性模块不会有主组件。特性模块的元数据中有一个 exports 属性,它的值是一个数组,用于告诉 Angular 该模块的哪些可声明对象可以在其它模块的组件模板中使用。
特性五:依赖注入
什么是依赖注入
依赖注入是一种设计模式,用于将类的依赖项注入到类的构造函数中。依赖注入的目的是为了解耦,使得类的依赖项可以在不同的环境中使用不同的实现,从而使得类的使用者不需要关心类的依赖项是如何创建的。
示例
1 | import { Injectable } from '@angular/core'; |
单例服务
单例服务是指在整个应用中只有一个实例的服务。
生成单例服务的两种方式
- 把 @Injectable() 中的 providedIn 属性设置为 “root”
- 把该服务包含在 AppModule 或某个只会被 AppModule 导入的模块中。
生命周期知识点
DestroyRef
什么是DestroyRef?
DestroyRef是一个类,它实现了OnDestroy接口,它的作用是用来处理一些资源的释放,比如订阅的取消,定时器的销毁等。
使用
1 | class Counter { |
ngZone.runOutsideAngular()
是什么
ngZone.runOutsideAngular()
是 Angular 框架提供的一个方法,它可以让你在 Angular 应用程序之外运行代码。具体来说,它会暂停 Angular 应用程序的变更检测,并在你的代码运行完毕后恢复变更检测。这个方法通常用于优化性能,例如在处理大量数据或执行复杂计算时,可以暂停 Angular 应用程序的变更检测,以避免不必要的性能开销。
实现原理
ngZone.runOutsideAngular()
的实现原理是通过重写 Zone.js 的 API 来实现的。Zone.js 是 Angular 应用程序的核心依赖,它会拦截所有的异步操作,并在异步操作开始和结束时执行一些代码。ngZone.runOutsideAngular()
会重写 Zone.js 的 API,以便在你的代码运行期间暂停变更检测。具体来说,它会重写 Zone.js 的 setTimeout()、setInterval() 和 requestAnimationFrame() 方法,以便在你的代码运行期间暂停变更检测。当你的代码运行完毕后,它会恢复 Zone.js 的 setTimeout()、setInterval() 和 requestAnimationFrame() 方法,以便继续变更检测。
当你使用 ngZone.runOutsideAngular()
方法时,你的代码会在 Angular 应用程序之外的 JavaScript 执行上下文中运行。Angular 应用程序通常会在一个单独的 JavaScript 执行上下文中运行,这个执行上下文被称为“NgZone”。当你的代码运行在 ngZone.runOutsideAngular()
方法中时,它会被包装在一个新的 JavaScript 执行上下文中,在这个执行上下文中,Angular 应用程序的变更检测被暂停,因此你的代码可以在不触发变更检测的情况下运行。这个新的执行上下文是在 Angular 应用程序之外运行的,因此它可以执行任何 JavaScript 代码,而不会影响 Angular 应用程序的状态或性能。