角理论
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
为自己总结的Angular理论知识。
一些需要了解的事情
HTML DOM
HTML 文档对象模型 (
DOM)。DOM 是网页的面向对象表示,可以使用 JavaScript 等脚本语言进行修改。延伸阅读
目录
Angular 简介
npm + typescript
Angular CLI 问题
Angular架构问题
- Angular 的架构是什么?
- Angular中的指令是什么?
- Angular 中的组件和模块
- Angular 中的装饰器是什么?
- Angular 中的模板是什么?
- Angular中的数据绑定是什么?
- 如何在Angular中实现单页应用程序(SPA)?
- Angular中的路由是什么?
- 如何在Angular中实现路由?
- 什么是延迟加载?
- 如何实现懒加载?
- Angular 中的服务是什么?
- 服务与组件
- 什么是依赖注入?
- 如何实现依赖注入
- 依赖注入的优势
子组件(ViewChild、ViewChildren、ContentChild、ContentChildren)
- ViewChild 和 ViewChildren
- 模板引用变量
- 什么是内容投射?
- ContentChild 和 ContentChildren
- 静态标志
- ViewChild、ViewChildren 与 ContentChild、ContentChildren
Angular 生命周期问题
HTTP
传递数据
管道
RxJS
什么是角度
JavaScript 框架。
主要功能:
帮助将视图(HTML)绑定到模型(对象)。这有助于开发者实现 MVC 框架。纯 JavaScript 需要编写大量代码。
主要特性:
- SPA(单页应用程序)。Angular 帮助开发者实现带有路由的单页应用程序。(URL 会变化,但只有一个 index.html 文件)
- 依赖注入。(Angular 帮助开发者实例化组件)例如,要将服务注入组件,只需将其放入构造函数中即可。
Angular 与 AngularJS
说实话,我不太确定,我尽量避免使用AngularJS哈哈
- Javascript 与 Typescript
- CLI 构建器
- 可以使用 Angular CLI 构建组件和服务
- 懒加载
- 仅在需要时加载特定模块。仅当用户导航到相应模块的路由时才会加载。查看更多
Angular中的指令是什么?
用于告诉 Angular 如何转换 HTML DOM 的 HTML 指令
3项主要指令(SAC)
- 组件指令
- 结构指令
- 属性指令
请注意:组件在技术上是一种指令。
组件指令
使用模板的指令。它类似于用户控件。
组件从技术上讲是一种指令。@Component它扩展了@Directive。
例如,声明一个自定义组件 my-grid。组件指令如下:<my-grid></my-grid>
更多信息请参阅文档。
结构指令
更改元素结构(添加/删除 DOM),
例如 NgIf、NgFor、NgSwitch(ngif 的 switch case 语句)
属性指令
更改元素的行为和外观
,例如 NgStyle、NgClass
阅读本文了解更多信息
NPM 和 node_modules 文件夹
NPM = Node.js 包管理器,
简化了 JS 框架库的安装。node_modules
= 所有包的安装目录,
它也像一个缓存。当开发者导入库时,如果没有指定路径,Node.js 会查找 node_modules 文件夹。
(摘自SitePoint和Stack Overflow
的全局与本地部署)
package.json 文件
它列出了所有 JS 引用(即项目的依赖项、库名称)。因此,开发人员可以一次性快速安装所有依赖项npm install。
可以通过以下命令从头开始重新创建 node_modules 目录。npm install
package.json 与 package-lock.json
package.json 文件记录应用程序所需库的最低版本。它还定义项目属性,例如作者等。package
-lock 文件仅用于将依赖项锁定到特定版本号。它记录每个已安装包的确切版本。
什么是 TypeScript?
Javascript 的超集。
- 为 JavaScript 添加类型
- 面向对象的编程环境,可编译成JS
这导致
- 更少的错误
- 更高的代码质量/生产力???
最终,TS 会被编译成 JS。
关于guru99的更多信息
- 错误会在编译时指出(代码需要编译)。
- TypeScript 使用类型和接口等概念来描述数据。
- TS需要编译。必须编译成JS。
Angular CLI 的重要性/优势
帮助开发人员生成样板代码,而不是从头开始编写。
- 生成现成的项目
ng new my-app(生成源文件、模块文件) - 生成现成的原理图(例如,组件/服务/指令)
ng generate service/component文档
Angular 中组件和模块的重要性
组件是 Angular 的主要构建模块。每个组件由以下部分组成:
- HTML模板(声明页面上呈现的内容)
- 定义行为的 TypeScript 类
- CSS 选择器,用于定义组件在模板中的使用方式
- 应用于模板的 CSS 样式(来自文档)
组件与模块
成分:
- 控件视图(html)
- 与其他组件和服务通信,为应用程序带来功能
模块包括:
- 组件的逻辑分组
参考:SO 答案
Angular 中的装饰器(注解/元数据)是什么?
告诉 Angular 这是一个什么类型的类。 例如,
在类声明上方: -> Angular 组件。- > Angular 模块 (也可能有人会问如何在 Angular 中创建组件和模块)**@Component@NgModule
在Angular中声明组件和模块时,必须使用装饰器。
Angular 中的模板是什么?
Angular 的 HTML 视图,您可以在其中编写指令
编写模板有两种方法:
@Component({template: <<here>>})内联(在指令中定义)- 单独的 HTML 文件(在组件 templateUrl 中定义)
更多信息请点击此处
Angular中的数据绑定是什么?
- 插值 ------
{{expression}} - 属性绑定-----
[target]=expression - 事件绑定--------
(target)=statement - 双向装订-----
[(ngModel)]
插值
数据从组件单向流向
视图。可以用 HTML 实现(单向)。
财产
组件单向数据传输。
数据从组件流向视图集的特定元素属性。
事件绑定
单向通信,从事件/视图到组件。
视图中的事件触发组件中的函数。监听元素更改事件。
双向绑定
双向绑定,结合了属性绑定和事件绑定。
在组件中设置事件。如果视图中发生某个事件,它可以触发组件中的函数。
例如,最常用于模板表单。
更多详情请参阅文档。
要使双向绑定生效,@Output必须使用模式,inputChange其中是属性input名称, 例如@Input
export class SizerComponent {
@Input() size: number | string;
@Output() sizeChange = new EventEmitter<number>();
resize(delta: number) {
this.size = Math.min(40, Math.max(8, +this.size + delta));
this.sizeChange.emit(this.size);
}
}
使用双向装订看起来像这样:
<app-sizer [(size)]="fontSizePx"></app-sizer>
请解释一下Angular的架构?
Angular 由 7 个主要构建模块组成:
- 模板(HTML视图)
- 组件(绑定视图和模型)
- 绑定(组件和模板如何相互通信)
- 模块(按逻辑对组件进行分组)
- 指令(改变 DOM 的样式/行为)
- 服务(在项目间共享通用逻辑)
- 依赖注入(将实例注入到构造函数中)(例如,将服务注入到组件中——只需将其放入组件构造函数中)
更多说明请参见文档。
什么是SPA?
单页应用
程序(SPA)是一种通过动态地从 Web 服务器获取新数据并重写当前网页来与用户交互的 Web 应用程序/网站。这
意味着主界面只需加载一次,其他功能则根据用户操作按需加载。
在生产环境中,这意味着只有一个包含 CSS 和 JavaScript 包的 index.html 文件。
这在需要丰富的客户端功能时尤为有利。由于页面完全重新加载的情况很少见,SPA 的加载速度通常更快。
例如,一个页面通常包含页眉、页脚和抽屉式导航栏。在单页应用程序 (SPA) 中,只会加载这些元素之间的部分,而不是整个页面。这样可以提高性能。
更多关于 SPA 的信息,请访问Angular 大学博客。
如何在Angular中实现单页应用程序(SPA)
路由
Angular路由是一个简单的集合,包含两部分:
- URL
- 调用 URL 时要加载的组件
它有助于定义 Angular 应用的导航。它将类似 URL 的路径映射到视图,而不是页面。因此,当用户从一个屏幕/URL 跳转到另一个屏幕/URL 时,应用会加载组件,而不是加载一个全新的页面。
Angular 中的路由是什么?
在 Angular 中,路由包含以下部分:
- 路径(URL路径)
- 成分
- (可选)重定向到路径
路由将 URL 路径映射到组件。
它帮助用户在应用程序中执行任务时从一个视图导航到另一个视图。
由于只有页面中的组件发生变化,而无需重新加载整个页面,因此路由有助于开发者更好地利用单页应用程序 (SPA)。
摘自:pluralsight和smashingmagazine
如何在Angular中实现路由?
- 在 Angular 中定义路由集合 --> 将 URL 路径映射到组件
- 定义路由器出口 --> 组件将显示在此处
- HTML 中的 [routerLink] 或 TS 文件中的 this.router.navigate(url) --> 允许用户导航到不同的路由/视图
什么是延迟加载
按需加载。
仅在需要时加载必要的 HTML、CSS 和 JS 文件。仅在需要时(例如,当用户执行特定操作时)加载其他组件。
如何实现懒加载?
- 将项目划分为多个独立模块
loadChildren在路由对象中声明属性
Angular 中的服务是什么?
该服务帮助开发人员在组件之间共享通用逻辑/通用功能
服务与组件
根据文档,
组件的目的是为了提升用户体验,仅此而已。组件应该:提供用于数据绑定的属性和方法,以在模板(视图)和应用程序逻辑(模型)
之间进行协调。
组件应将其他任务(例如从服务器获取数据、验证用户输入)委托给服务。服务中的任务也可以通过依赖注入
与其他组件共享。
什么是依赖注入?
应用设计模式: 开发者无需创建对象实例(无需实例化服务,Angular 通过构造函数注入)。
let service: ExampleService = new ExampleService()
export class ExampleComponent{
constructor (private service: ExampleService) {
this.service.doSomething();
}
}
摘自文档
如何在Angular中实现依赖注入?
提供服务有三种方式。
- 根服务在根级别提供,Angular 会创建一个共享的(即单例)服务实例,并将其注入到任何请求该服务的类中。
可注射根部装饰器在服务类中提供。
@Injectable({
providedIn: 'root',
})
这是自动生成的,ng generate service
仅向特定模块提供服务,更改root到ExampleModule
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'any',
})
export class UserService {
}
所有providedIn: any预加载的实例共享一个单例实例,而延迟加载的模块则各自拥有一个唯一的实例。
providers在 NgModule中,在数组中提供模块注册服务
@NgModule({
providers: [
ExampleService,
Logger
],
...
})
如果 NgModule 是根 AppModule,ExampleService它将是单例模式,并在整个应用程序中可用。
参考
AppModule 通过在AND延迟加载模块中导入服务,应用程序可以生成服务的多个实例。
- 在组件级别注册提供程序,每个新实例都会获得一个新的服务实例。
@Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
更多阅读资料请参阅相关文档。
依赖注入的优势
解耦类依赖关系。
可以在不修改所有组件类代码的情况下更改服务类的依赖关系。如果服务类的实例化位于组件中,则必须修改所有服务实例化代码。
也就是说,只需更改服务构造函数中的参数即可,无需更改其他任何地方。
ng serve 与 ng build
ng serve- 在内存中构建 Angular 应用程序ng build- 在硬盘上构建应用程序。将所有编译后的代码放在/dist文件夹中
--prod 标志?
--prod该标志确保您构建的应用程序可用于生产环境。它会压缩 JS 文件,移除注释,使应用程序做好生产准备。
组件生命周期的重要性?
指令也有生命周期。
简而言之,开发者可以通过实现生命周期钩子
接口 来调用组件生命周期的关键事件并编写自定义代码。(例如:)更多详情请参阅文档。ngOnInit()
组件实例具有生命周期,从 Angular 实例化组件类并渲染组件视图,直到 Angular 销毁组件并从 DOM 中删除渲染的模板。
开发人员可以使用生命周期钩子方法来接入生命周期的关键事件,以初始化新实例/在删除实例之前进行清理。
组件生命周期中的事件和顺序?
Shivani 在 Medium 上发表了一篇很棒的文章,深入探讨了 Angular 的生命周期。
两种类型的事件
- 组件首次加载时触发的事件
- 变更检测触发事件
生命周期事件列表
(仅发生一次的事件以斜体显示)
- 构造函数(更像是 TypeScript 事件,而不是 Angular 事件)
- ngOnChanges(也称为数据绑定输入属性
@Input更改时调用的事件)。此事件发生频率非常高。 - ngOnInit(屏幕上显示的数据绑定属性/设置组件
@Input属性) - ngDoCheck()(在每次变更检测运行时,ngOnChanges() 之后立即触发)
- ngAfterContentInit(在 Angular 将外部内容投影到组件视图之后)与
<ng-check> - ngAfterContentCheck(检查投影内容)
- ngAfterViewInit(在 Angular 初始化组件视图及其子视图之后)
- ngAfterViewChecked(在 Angular 检查组件的视图及其子视图之后)
- ngOnDestroy(Angular 销毁组件前的清理操作)
三部分
- 启动事件(变更检测)(ngOninit,ngDoCheck)
- 投影内容事件触发(ngAfterContentInit,ngAfterContentChecked)
- 组件视图事件触发(ngAfterViewInit、ngAfterViewChecked)
构造函数与 ngOnInit()?
- 构造函数 - TypeScript 概念,由 TypeScript 调用
- ngOnInit - Angular 概念/事件,由 Angular 框架调用
顺序:
- 构造函数是火。
- 视图和组件已初始化,绑定已完成。
- ngOnInit(组件初始化后,访问 DOM 元素)
构造函数:
- 初始化类变量
- 依赖注入
ngOnInit:
- 访问类变量
- 访问
@Input变量
访问@Input或从构造函数访问将给出undefined。
更多信息仅在该SO 答案中提供。
@ViewChildngAfterViewInit
ViewChild 和 ViewChildren
@ViewChild:引用组件中的一个@ViewChildrenHTML 元素/DOM 元素:引用元素集合
如果检测到多个元素@ViewChild,则只会引用第一个元素。
模板引用变量
模板变量帮助开发者在模板的另一部分中使用来自模板一部分的数据。
<input #phone placeholder="phone number" />
<!-- lots of other elements -->
<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>
要将模板引用变量与 ViewChild 一起使用
<div #div1> this div </div>
@Viewchild('div1',{static: false}) div: ElementRef
更多信息请点击此处
什么是内容投射?
投影:将父组件的 HTML 内容/组件内容投影到子组件。
<ng-content></ng-content>
允许您将影子 DOM 作为输入插入到组件中。
@Input仅允许您向组件传递简单的字符串/对象。<ng-content>允许您传递 HTML 元素或其他组件。
<app-component>
<h1>Header</h1>
<p>paragraph</p>
</app-component>
多内容投影:内容投影槽
GreetComponent
<div class="container">
<ng-content> </ng-content>
<ng-content select="h1"></ng-content>
<p>welcome text</p>
<ng-content select=".content"></ng-content>
</div>
父内容
<div>
<greet>
<h3> everything else </h3>
<h1> Header </h1>
<span class="content"> something </span>
</greet>
</div>
内容投影后的输出是
<div>
<div class="container">
<h3> everything else </h3>
<h1> Header </h1>
<p>welcome text</p>
<span class="content"> something </span>
</div>
</div>
内容儿童和内容儿童
访问投影内容
CardComponent.html
<div class="card">
<ng-content select="header"></ng-content>
<ng-content select="content"></ng-content>
<ng-content select="footer"></ng-content>
</div>
CardComponent.ts
@ContentChild("header") cardContentHeader: ElementRef;
ParentComponent.html
<card>
<header><h1 #header>Angular</h1></header>
<content>One framework. Mobile & desktop.</content>
<footer><b>Super-powered by Google </b></footer>
</card>
tektutorialshub提供了一份简明扼要的说明。
静态标志
static 默认设置为 false。
之所以设置为 false,是因为我们希望每次变更检测都能解析查询。
Angular 文档和Stack Overflow 上对该标志有更详细的描述。
ViewChild、ViewChildren、ContentChild、ContentChildren
ViewChild:从自身视图访问 DOM 元素; ContentChild:从另一个
视图 访问 DOM 元素,或访问由其他人投影的 DOM 元素 (例如,元素位于 CardComponent.ts 中,但投影内容来自 ParentComponent.html)。@ContentChild
ViewChildren:ViewChild 的集合;
ContentChildren:ContentChildren 的集合
在 Angular 中发起 HTTP 请求
- 从
@angular/common/http - 在 Angular 模块中导入 HttpModule
- 通过依赖注入创建 HttpClient 对象
constructor(public http:HttpClient) - 使用 HttpClient 创建 POST/GET 类
this.http.post(URL, data).subscribe(res => successfunc(res), err => errfunc(err) - 订阅 Http.post / Http.get。Subscribe 接受两个输入:成功函数和错误函数。
需要订阅功能
如果 HTTP 调用成功/失败,则需要提供成功/错误处理方法。
此外,如果没有异步管道,则需要订阅才能使可观察对象执行。
处理 HTTP 调用中的错误
- 订阅方法的错误回调
catchError管道中的函数。(catchError 必须返回一个新的 Observable 或抛出一个错误)当抛出新的错误时,抛出的错误会传递给订阅者。更多详情请参见tekturotials。
第二种方法在 Angular 中实现了关注点分离
。 此外,
还可以链式调用多个可观察对象(一个接一个)。
catchError保持可观察对象存活。如果内部可观察对象出现错误,外部可观察对象仍会继续执行(因为它接收到了可观察对象)。合并多个可观察对象(执行多个可观察对象)。- 允许可观察对象数组完成。否则,如果只有一个可观察对象失败,则所有可观察对象都会失败,而
catchError允许该可观察对象完成并发出一些信息。
在组件和路由之间传递数据
-
父子组件
@Input:父组件 -> 子组件@Output:子组件 -> 父组件@ViewChild: -
路由间的数据 -> 路由查询参数
this.route.navigate(['url'],
{queryParams: {
name: 'myname'
}
});
- 服务更多地侧重于全球数据/存储更多地通过单例服务共享全球数据
- 创建服务
- 将服务注入组件
-
访问服务数据
-
浏览器
*全局数据 *
本地存储/临时存储
使用服务传递数据的良好实践
共享数据与传递数据:
如果您只是在组件之间传递数据,则应避免使用服务/全局变量。
其他组件可能会修改全局变量,导致代码变得非常混乱。
为什么需要角形管?
管道用于转换 Angular UI 表达式中的数据。Angular
内置了一些管道:
- AsynPipe:从异步源读取对象(默认情况下是不纯的)
- JsonPipe:将对象转换为 JSON 字符串
- KeyValuePipe:可在 ngFor 中用于遍历对象的键和值。
- DatePipe:格式日期
- CurrencyPipe:数字 -> 货币字符串
例子:
<p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </p>
你还可以串联管道(先实现第一个管道,然后将第二个管道的输出连接到第一个管道的输出)。
{{ birthday | date | uppercase}}
如何在Angular中创建自定义管道?
- 用于
@Pipe将类标记为管道 - 实现 PipeTransform 接口
- 实现 transform 方法。
@Pipe({name: 'uselessPipe'})
export class uselessPipe implements PipeTransform {
transform(value: string, before: string, after: string): string {
let newStr = `${before} ${value} ${after}`;
return newStr;
}
}
{{ user.name | uselessPipe:"Mr.":"the great" }}
角形管道中的变化检测。
-
数据可以是原始输入值(字符串、数字)或对象引用(日期/数组)。Angular
会在检测到引用的输入值发生变化时执行管道操作。 -
修改复合对象内部的内容(例如日期中的月份、数组元素、对象属性)
可以使用impure管道符。
参考自 Angular文档
检测对基本元素和对象引用的纯粹更改
Angular 管道默认是纯函数。Angular仅在检测到输入值发生纯变化时才会执行管道操作。纯变化是指:
- 更改为 pimitive 输入值(
string,,,)numberbooleansymbol - 更改对象引用(
Date,,,)ArrayFunctionObject
在纯管道中,Angular 会忽略组件对象内部的变化(例如,向现有数组添加新元素),因为对象引用没有改变。
例子:
<div *ngFor="let hero of (heroes | flyingHeroes)">
{{hero.name}}
</div>
@Pipe({ name: 'flyingHeroes' })
export class FlyingHeroesPipe implements PipeTransform {
transform(allHeroes: Hero[]) {
return allHeroes.filter(hero => hero.canFly);
}
}
如果用户添加了飞行英雄,由于数组引用没有改变,Angular 不会更新显示,因此飞行英雄的显示不会更新。有两种方法可以解决这个问题。
- 更改对象引用,将数组替换为包含新更改元素的新数组,并将新数组输入到管道。
- 使管道检测不纯更改,并将纯净标志设置为 false
@Pipe({
name: 'flyingHeroesImpure',
pure: false
})
什么是变化检测
Angular 将应用程序 UI 状态与数据状态同步的机制
当组件数据更新时(例如,来自用户事件/应用程序逻辑),视图中绑定到 DOM 属性的值可能会发生变化。变更检测器负责更新视图以反映当前的数据模型。
参考资料:Angular文档
同步与异步
同步代码按顺序执行。
当一条语句正在执行时,其他语句都无法执行。
异步代码无需等待——你的程序可以持续运行。
异步代码在同步 JavaScript 中很难实现,开发者无法确定代码需要多长时间才能完成(例如,从 API 下载图片需要多长时间?)。
因此,像这样的代码:
let response = fetch('myImage.png'); // fetch is asynchronous
let blob = response.blob();
// display your image blob in the UI somehow
blob 可能未定义。
异步 JavaScript 主要有两种代码风格:
- 回调函数(旧式)
- 承诺(新式)
异步 JavaScript 有两种模型:(更多详情请参见RxJS)
- 拉取模式(消费者为王,消费者决定何时从生产者接收数据,生产者不知道数据何时交付)
- 推送模式(生产者为王,决定何时向消费者发送数据)
Promise 和 Observable 都遵循推送模型。Promise
(生产者)将解析后的值传递给已注册的回调函数(消费者)。
什么是 RxJS?
RxJS 代表JavaScript 的响应式扩展。
它用于处理异步数据流,
例如异步数据源(HTTP 数据、定时器)可能需要 1 秒或 15 秒才能到达。因此,应用程序应该有一个监听器
(例如,HTTP 响应,数据以较小的时间间隔到达)。
RxJs 可以帮助您轻松处理异步数据流。
什么是可观测变量和观察者?
Observable 是 JavaScript 的一个推送系统。它是一个生产多个值并将其“推送”给观察者(消费者)的机制。
Observable 可以同步或异步地提供多个值。
这篇freecodecamp文章有非常全面的介绍。
可观测性分为4 个主要阶段
- 创建
- 订阅
- 执行
- 处置
RxJS 中 subscribe 的重要性
每次调用都会observable.subscribe为指定的订阅者触发独立的设置。RxJS :启动一个“可观察执行”,并将值/事件传递给执行的观察者。Angular :只有当有人订阅时,可观察实例才会开始发布
值
。
如何取消订阅(可观测对象的销毁阶段)
摘自RxJS文档
const subscription = observable.subscribe(x => console.log(x));
subscription.unsubscribe();
由于 Observable 的执行次数可能无限循环,而观察者通常会在有限时间内中止执行,因此我们需要一个用于取消执行的 API。当执行停止/取消时,我们可以避免浪费计算能力/内存资源。
用示例代码解释运算符的概念
运算符也是函数。
运算符分为两种类型:
- Pipeable 操作符以可观察对象作为输入,输出也是一个可观察对象。预处理逻辑可以转换可观察对象中的数据。
- 创建运算符可以作为独立函数调用,以创建新的 Observable。
摘自RxJS文档
文章来源:https://dev.to/jing/angular-theory-553p

