简介
- 完整的 MVC 框架:Angular 提供了一个完整的解决方案,包括路由、依赖注入等。
- 强类型支持:Angular 使用 TypeScript 进行开发,提供强大的静态类型检查。这意味着如果您知道如何使用 JavaScript,特别是 ECMAScript 6/7 进行编码,那么转换应该是无懈可击的 。
- 强大的工具集:Angular 内建了许多工具,如 Angular CLI,简化了开发流程。
- 可扩展性:由于其设计和工作方式,Angular 很容易就能扩展 。
现状
国内很少人使用 Angular 的原因:
- 一是 AngularJS 跟 Angular2 的断崖是更新
- 二是 后续的升级太快
- 三是国内缺乏迷你 Angular 的轮子,导致庞大的 Angular 无法塞进小程序中
Angular 中的 JIT 与 AOT
Angular 中的 JIT 与 AOT编译可以选择放在两个时机执行:
- 代码构建时,被称为 AOT(Ahead Of Time,提前编译或预编译),宿主环境获得的是编译后的代码 (进行语法,变量引用等判断,早期发现错误)
- 代码在宿主环境执行时,被称为 JIT(Just In Time,即时编译),代码在宿主环境编译并执行
项目组成
- 根模块 + 特性模块(module)
NgModules
@NgModule
的参数是一个元数据对象,用于描述如何编译组件的模板,以及如何在运行时创建注入器
import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'
import { AppComponent } from './app.component'
@NgModule({
declarations: [AppComponent], // 用于规定哪些组件、指令和管道属于它(declarations)
imports: [BrowserModule], // 标记它使用了哪些其它模块(imports)
providers: [], // 在当前模块的注入器中可用的一组可注入对象。
bootstrap: [AppComponent], // 引导此模块时引导的一组组件
})
export class AppModule {}
共享模块
- 必须导入
CommonModule
,因为该模块需要一些常用指令。 - 声明并导出一些工具性的管道、指令和组件类。
import { CommonModule } from '@angular/common'
import { NgModule } from '@angular/core'
import { CustomerComponent } from './customer.component'
import { OrdersPipe } from './orders.pipe'
@NgModule({
imports: [CommonModule],
declarations: [CustomerComponent, OrdersPipe],
exports: [CustomerComponent, OrdersPipe, CommonModule],
})
export class SharedModule {}
依赖注入
依赖注入(DI)
,允许带有 Angular 装饰器的类(例如组件、指令、管道和可注入对象)配置它们所需的依赖项。
添加 @Injectable
装饰器以表明此类可以被注入
@Injectable({
providedIn: 'root',
})
class HeroService {}
@Component({ … })
class HeroListComponent {
// 根据类型自动注入
constructor(private service: HeroService) {}
}
注入的三种作用域
- 全局
root
- 模块
xxxModule
- 组件级别
多例 和 单例
当 Angular 的路由器惰性加载一个模块时,它会创建一个新的注入器。 形成了多例
在 Angular 中有两种单例
服务方式来生成:
把
@Injectable()
中的providedIn
属性设置为"root"
把该服务包含在 AppModule 根模块 或
某个只会被
AppModule 导入的模块中。
第三种
:forRoot
如果
模块同时定义
了 providers(服务)和 declarations(组件、指令、管道),那么,当你同时在多个特性模块中加载此模块时,这些服务就会被注册在多个地方。这会导致出现多个服务实例,并且该服务的行为不再像单例一样。
RouterModule
你可以多次导入此 NgModule,对于每个惰性加载的包导入一次。但是,只能有一个 Router 服务是活动的。为确保这一点,在导入此模块时有两种方法来注册路由:
forRoot()
方法会创建一个 NgModule,其中包含所有指令、给定的路由以及Router 服务本身
。forChild()
方法会创建一个 NgModule,其中包含所有指令和给定的路由,但不包括 Router 服务
。
组件
@Component({
selector: 'app-root',
template: `
<h1>Tour of Heroes</h1>
<app-hero-main [hero]="hero"></app-hero-main>
`,
styles: ['h1 { font-weight: normal; }'],
})
export class HeroAppComponent {
/* . . . */
}
交互
- Input 、Output
@Input() hero!: Hero;
@Output() voted = new EventEmitter<boolean>();
- 获取页面元素
@ViewChild
<div #editor></div>
import { ViewChild } from '@angular/core'
class Comp {
// @ViewChildren('items') // 会获取所有#items 将得到一个数组
@ViewChild('editor', { static: true }) editor
getDom() {
this.editor.nativeElement // 获取真实dom节点
}
}
- 服务交互
Subject
CSS 的使用
组件内联
styles: ['h1 { font-weight: normal; }']
styleUrls: ['./app.component.scss']
两个特殊标记
- :host 伪类选择器可用于创建针对宿主元素自身的样式
- :host-context 它在当前组件宿主元素的祖先节点中查找 CSS 类,直到文档的根节点为止。它只能与其它选择器组合使用。
ngStyle 、 ngClass
<some-element [ngStyle]="{'font-style': styleExp}">...</some-element>
<some-element [ngClass]="{'first': isActive}">...</some-element>
生命周期函数
constructor
ngOnChanges
如果有输入属性,会优先于OnInit
, 检测不到对象属性变化ngOnInit
第一次接收到组件属性ngDoCheck
可以检测到任意属性变化,包括,对象属性变化ngAfterContentInit
插槽内容填充完成ngAfterContentChecked
ngAfterViewInit
组件视图和子视图加载完成ngAfterViewChecked
组件视图和子视图更新完成
ngOnDestroy
export class MyeditorComponent implements OnInit, OnChanges, OnDestroy {
ngOnInit() {} // 页面加载完成时做的事情
ngOnChanges(changes: SimpleChanges): void {
// key 是传入的属性 key
console.log(changes[key].currentValue)
} // 获取变化的 Input
ngOnDestroy(): void {} // 做一些销毁的事情
}
ngModel 自定义实现
<HelloWorld [(editorContent)]="name" />
import { EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'
class Person {
@Input() editorContent
@Output() editorContentChange: EventEmitter<any> = new EventEmitter()
handle() {
this.editorContentChange.emit('更新后的内容')
}
}
自定义指令
需求:为元素设置默认背景颜色,鼠标移入时的背景颜色以及移出时的背景颜色。
<div [appHover]="{ bgColor: 'skyblue' }">Hello Angular</div>
import { AfterViewInit, Directive, ElementRef, HostListener, Input } from "@angular/core"
interface Options { bgColor?: string }
@Directive({ selector: "[appHover]" })
export class HoverDirective implements AfterViewInit {
// 接收参数
@Input("appHover") appHover: Options = {}
// 要操作的 DOM 节点
element: HTMLElement
// 获取要操作的 DOM 节点
constructor(private elementRef: ElementRef) {
this.element = this.elementRef.nativeElement
}
// 组件模板初始完成后设置元素的背景颜色
ngAfterViewInit() {
this.element.style.backgroundColor = this.appHover.bgColor || "skyblue"
}
// 为元素添加鼠标移入事件
@HostListener("mouseenter") enter() {
this.element.style.backgroundColor = "pink"
}
// 为元素添加鼠标移出事件
@HostListener("mouseleave") leave() {
this.element.style.backgroundColor = "skyblue"
}
}
自定义管道 Pipe
@Pipe({
name: 'subString',
})
export class subStringPipe implements PipeTransform {
transform(value: any, ...args: any[]) {
if (value.length > 10) {
console.log(value)
let len = args[0] || 8
return value.substring(0, len) + '...'
}
return value
}
}
<div>{{ name | subString:8 }}</div>
ng-template 模板传参复用
@Component({
selector: 'ng-template-outlet-example',
template: `
<ng-container *ngTemplateOutlet="eng; context: myContext"></ng-container>
<hr />
<ng-container *ngTemplateOutlet="svk; context: myContext"></ng-container>
<hr />
<ng-template #eng let-name>
<span>Hello {{ name }}!</span>
</ng-template>
<ng-template #svk let-person="localSk">
<span>Ahoj {{ person }}!</span>
</ng-template>
`,
})
export class NgTemplateOutletExample {
myContext = { $implicit: 'World', localSk: 'Svet' }
}
Angular 原理
- Angular 变更检测和运行时优化
- Angular 会从上到下遍历你的组件,寻找更改。Angular 会定期运行其变更检测机制,以便对数据模型的更改反映在应用程序的视图中。
- 变更检测可以手动触发,也可以通过异步事件(比如用户交互或 XHR 自动完成)来触发。
NgZone
- zone.js 是一种信号机制,Angular 用它来检测应用程序状态何时可能已更改。
- 它捕获异步操作,比如 setTimeout、网络请求和事件侦听器。Angular 会根据来自 Zone.js 的信号安排变更检测
class Comp{
constructor(private ngZone: NgZone) {}
ngOnInit(): void {
// setInterval(() => {
// this.num++
// }, 1000)
this.ngZone.run(() => {
this.num++
})
this.ngZone.runOutsideAngular(() => {
setInterval(() => {
console.log('异步执行了') // 控制台打印
this.extraNum++ // 页面不会体验值增加,但是如果其他事件触发了,组件最新的值会被检查到页面上,ps: 上面setInterval 放开
}, 1000)
})
}
}
常见的数据变化时机:
- 组件初始化
- 事件监听器
- http 请求、websocket
- 宏任务、微任务
- 其他异步操作