Skip to content

简介

  1. 完整的 MVC 框架:Angular 提供了一个完整的解决方案,包括路由、依赖注入等。
  2. 强类型支持:Angular 使用 TypeScript 进行开发,提供强大的静态类型检查。这意味着如果您知道如何使用 JavaScript,特别是 ECMAScript 6/7 进行编码,那么转换应该是无懈可击的 。
  3. 强大的工具集:Angular 内建了许多工具,如 Angular CLI,简化了开发流程。
  4. 可扩展性:由于其设计和工作方式,Angular 很容易就能扩展 。

现状

国内很少人使用 Angular 的原因:

  • 一是 AngularJS 跟 Angular2 的断崖是更新
  • 二是 后续的升级太快
  • 三是国内缺乏迷你 Angular 的轮子,导致庞大的 Angular 无法塞进小程序中

Angular 中的 JIT 与 AOT

Angular 中的 JIT 与 AOT编译可以选择放在两个时机执行:

  • 代码构建时,被称为 AOT(Ahead Of Time,提前编译或预编译),宿主环境获得的是编译后的代码 (进行语法,变量引用等判断,早期发现错误)
  • 代码在宿主环境执行时,被称为 JIT(Just In Time,即时编译),代码在宿主环境编译并执行

项目组成

  • 根模块 + 特性模块(module)

NgModules

@NgModule 的参数是一个元数据对象,用于描述如何编译组件的模板,以及如何在运行时创建注入器

js
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,因为该模块需要一些常用指令。
  • 声明并导出一些工具性的管道、指令和组件类。
js
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 装饰器以表明此类可以被注入

js
@Injectable({
  providedIn: 'root',
})
class HeroService {}
js
@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 服务

组件

js
@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 {
  /* . . . */
}

交互

  1. Input 、Output
  • @Input() hero!: Hero;
  • @Output() voted = new EventEmitter<boolean>();
  1. 获取页面元素 @ViewChild
html
<div #editor></div>
js
import { ViewChild } from '@angular/core'

class Comp {
  // @ViewChildren('items') // 会获取所有#items  将得到一个数组
  @ViewChild('editor', { static: true }) editor

  getDom() {
    this.editor.nativeElement // 获取真实dom节点
  }
}
  1. 服务交互 Subject

CSS 的使用

组件内联

  1. styles: ['h1 { font-weight: normal; }']
  2. styleUrls: ['./app.component.scss']

两个特殊标记

  • :host 伪类选择器可用于创建针对宿主元素自身的样式
  • :host-context 它在当前组件宿主元素的祖先节点中查找 CSS 类,直到文档的根节点为止。它只能与其它选择器组合使用。

ngStyle 、 ngClass

html
<some-element [ngStyle]="{'font-style': styleExp}">...</some-element>
<some-element [ngClass]="{'first': isActive}">...</some-element>

生命周期函数

  1. constructor
  2. ngOnChanges 如果有输入属性,会优先于 OnInit, 检测不到对象属性变化
  3. ngOnInit 第一次接收到组件属性
  4. ngDoCheck 可以检测到任意属性变化,包括,对象属性变化
    • ngAfterContentInit 插槽内容填充完成
    • ngAfterContentChecked
    • ngAfterViewInit 组件视图和子视图加载完成
    • ngAfterViewChecked 组件视图和子视图更新完成
  5. ngOnDestroy
js
export class MyeditorComponent implements OnInit, OnChanges, OnDestroy {
  ngOnInit() {} // 页面加载完成时做的事情
  ngOnChanges(changes: SimpleChanges): void {
    // key 是传入的属性 key
    console.log(changes[key].currentValue)
  } // 获取变化的 Input

  ngOnDestroy(): void {} // 做一些销毁的事情
}

ngModel 自定义实现

html
<HelloWorld [(editorContent)]="name" />
js
import { EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'
class Person {
  @Input() editorContent
  @Output() editorContentChange: EventEmitter<any> = new EventEmitter()

  handle() {
    this.editorContentChange.emit('更新后的内容')
  }
}

自定义指令

需求:为元素设置默认背景颜色,鼠标移入时的背景颜色以及移出时的背景颜色。

html
<div [appHover]="{ bgColor: 'skyblue' }">Hello Angular</div>
js

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

js
@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
  }
}
html
<div>{{ name | subString:8 }}</div>

ng-template 模板传参复用

js
@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 原理

  1. Angular 变更检测和运行时优化
  2. Angular 会从上到下遍历你的组件,寻找更改。Angular 会定期运行其变更检测机制,以便对数据模型的更改反映在应用程序的视图中。
  3. 变更检测可以手动触发,也可以通过异步事件(比如用户交互或 XHR 自动完成)来触发。

NgZone

  1. zone.js 是一种信号机制,Angular 用它来检测应用程序状态何时可能已更改。
  2. 它捕获异步操作,比如 setTimeout、网络请求和事件侦听器。Angular 会根据来自 Zone.js 的信号安排变更检测
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)
    })
  }
}

常见的数据变化时机:

  1. 组件初始化
  2. 事件监听器
  3. http 请求、websocket
  4. 宏任务、微任务
  5. 其他异步操作