Angular 基础整理

2,910 阅读11分钟

Angular 基础整理 - 入门

Angular实现语法 typescript .

Angular基本构造块是 NgModule : 会把相关的代码收集到一起。Angular应用就是由一组 NgModule 定义出的。应用至少会有一个用于引导应用的跟模块,通常还会有很多特性模块:

  • 组件定义视图
    • 视图是一组课件的屏幕元素,可以根据程序逻辑和数据展示、修改
    • 每个应用只收有一个根组件
  • 组件使用服务
    • 与视图不相关的业务处理逻辑应该置于服务中
    • 服务可以作为依赖被注入到组件中,可以使代码更加模块化、增强复用、更加高效

组件和服务本质上都是类,一般使用 装饰器 来标志类型

  • 组件类的元数据将组件类和一个用来定义视图的模板关联起来。

    @Component({ // 元数据
      selector: 'app-base-info',
      templateUrl: './base-info.component.html',
      styleUrls: ['./base-info.component.scss']
    })
    
    • 模板把普通的HTML和Angular 指令绑定标记 组合起来,可以使Angular在呈现HTML之前先修改这些HTML
  • 服务类的元数据提供一些信息,Angular要用这些信息让组件可以通过依赖注入使用该服务

    @Injectable({ // 元数据
      providedIn: 'root'
    })
    

模块

NgModule 为一个组件集声明了编译的上下文环境,专注于某个应用领域、某个工作流或一组紧密相关的能力。将其中的组件和一组相关代码(比如服务)关联起来,形成功能单元。

每个Angular都有一个跟模块,通常命名为 APPModule 。根模块提供了用来启动应用的引导机制。

一个应用通常包含很多个功能模块

两个 NgModule 之间可以相互调用功能【被调用的功能要提前被导出】

NgModule

Angular 应用是模块化的,它拥有自己的模块化系统,称作 NgModule 。 一个 NgModule 就是一个容器,用于存放一些内聚的代码块,这些代码块专注于某个应用领域、某个工作流或一组紧密相关的功能。 它可以包含一些组件、服务提供商或其它代码文件,其作用域由包含它们的 NgModule 定义。 它还可以导入一些由其它模块中导出的功能,并导出一些指定的功能供其它 NgModule 使用。

@NgModule({
    providers: Provider[], 
    /*
    在当前模块的注入器中可用的一组可注入对象。
    - 在这里列出了提供商的依赖项可用于注入到任何组件、指令、管道或该注入器下的服务。 
    - 引导用的 NgModule 使用的是根注入器,可以为应用中的任何部件提供依赖。
    */
    declarations: Array<Type<any> | any[]>, 
    /*
    属于该模块的一组组件、指令和管道(统称可声明对象)。
    */
    imports: Array<Type<any> | ModuleWithProviders<{}> | any[]>,
    /*
    这里列出的 NgModule 所导出的可声明对象可用在当前模块内的模板中
    */
    exports: Array<Type<any> | any[]>, // 也可导出其他Module。。。
    /*
    此 NgModule 中声明的一组组件、指令和管道可以在导入了本模块的模块下任何组件的模板中使用。 导出的这些可声明对象就是该模块的公共 API。
    - 可声明对象应该且只能属于一个 NgModule。 一个模块可以列出在它的 exports 中列出一些其它模块,这些模块中所有公开的可声明对象也同样会导出。
    - 默认情况下,可声明对象是私有的。 如果 ModuleA 不导出 UserComponent,那么只有这个 ModuleA 中的组件才能使用 UserComponent。
    - 导出具有传递性。ModuleA 可以导入 ModuleB 并将其导出,这会让所有 ModuleB 中的导出同样可用在导入了 ModuleA 的那些模块中。
    - 根模块没有任何理由导出任何东西,因为其它模块永远不需要导入根模块。
    */
    entryComponents: [], // Module内的公共组件需要在这里导出
    /*
    定义此 NgModule 中要编译的组件集,这样它们才可以动态加载到视图中。
    */
    bootstrap: [],
    /*
    只有根模块才应该设置这个 bootstrap 属性。
    当该模块引导时需要进行引导的组件。列在这里的所有组件都会自动添加到 entryComponents 中。
    */
    schemas: [],
    /*
    该 NgModule 中允许使用的声明元素的 schema(HTML 架构)。 元素和属性(无论是 Angular 组件还是指令)都必须声明在 schema 中。
    */
    id: string,
    /*
    当前 NgModule 在 getModuleFactory 中的名字或唯一标识符。 如果为 undefined,则该模块不会被注册进 getModuleFactory 中。
    */
    jit: true
	/*
	如果为 true,则该模块将会被 AOT 编译器忽略,因此始终会使用 JIT 编译。
	这是为了支持未来的 Ivy 渲染器,目前没什么用。
	*/
})

组件

每个Angular至少有一个组件【根组件】。

每个组件都会定义一个类,包含应用的数据、逻辑、HTML模板

@Component({
  selector: 'app-base-info',
  templateUrl: './base-info.component.html',
  styleUrls: ['./base-info.component.scss']
})

@Component() 装饰器表明 紧随 它的那个类是一个件,并提供模板和该组件专属的元数据。

模板、指令和数据绑定

模板灰板HTML和Angular的标记(markup)组合起来,这些标记可以在HTML元素显示出来之前修改它们。模板中的指令会提供程序逻辑,而绑定标记会把应用中的数据和DOM连接在一起。有两种类型的数据绑定:

  • 事件绑定 让应用可以通过更新应用的数据来响应目标环境下的用户输入
  • 属性绑定 可以将处理过的数据插入到HTML中

模板也可以通过管道转换要显示的值以增强用户体验【一般用来格式化数据】

模板与视图

你要通过组件的配套模板来定义其视图。模板就是一种 HTML,它会告诉 Angular 如何渲染该组件。

视图通常会分层次进行组织,让你能以 UI 分区或页面为单位进行修改、显示或隐藏。 与组件直接关联的模板会定义该组件的 宿主视图。该组件还可以定义一个带层次结构的视图,它包含一些内嵌的视图作为其它组件的宿主。

双向数据绑定

数据绑定标记的四种形式:

  • {{ value }} 组件 => DOM
  • [property]="value" 组件 => DOM
  • (event)="handler" DOM => 组件
  • [(ng-mudel)]="property" 组件 <=> DOM

管道

在模板中声明显示值的转换逻辑。带有 @pipe 装饰器的类中会定义一个转换函数,用来把输入值转换成供视图先试用的输出值。Angular内置管道: Pipes API 列表

使用方式:{{interpolated_value | pipe_name}}

指令

Angular 的模板是 动态的 。当 Angular 渲染它们的时候,会根据 指令 给出的指示对 DOM 进行转换。 指令就是一个带有 @Directive() 装饰器的类。

组件从技术角度上说就是一个指令,但是由于组件对 Angular 应用来说非常独特、非常重要,因此 Angular 专门定义了 @Component() 装饰器,它使用一些面向模板的特性扩展了 @Directive() 装饰器。

除组件外,还有两种指令: 结构型指令属性型指令。 Angular 本身定义了一系列这两种类型的指令,你也可以使用 @Directive() 装饰器来定义自己的指令。

像组件一样,指令的元数据把它所装饰的指令类和一个 selector 关联起来,selector 用来把该指令插入到 HTML 中。 在模板中,指令通常作为属性出现在元素标签上,可能仅仅作为名字出现,也可能作为赋值目标或绑定目标出现。

结构型指令

结构型指令 通过添加、移除或替换DOM元素来修改布局。

  • *ngFor
  • *ngIf

属性型指令

属性型指令 会修改现有元素的外观或行为。

服务与依赖注入

服务

服务 是一个广义的概念,它包括应用所需的任何值、函数或特性。狭义的服务是一个明确定义了用途的类。它应该做一些具体的事,并做好。与特定视图无关希望跨组件共享数据、逻辑 情况下可以创建服务类。

理想情况下,组件的工作只管用户体验,而不用顾及其它。 它应该提供用于数据绑定的属性和方法,以便作为视图(由模板渲染)和应用逻辑(通常包含一些 模型 的概念)的中介者。

服务类的定义通常紧跟在 Injectable() 装饰器之后。该装饰器提供的元数据可以让服务作为依赖 被注入到 客户组件中。

对于要用到的任何服务,你必须至少注册一个提供商。服务可以在自己的元数据中把自己注册为提供商,这样可以让自己随处可用。或者,你也可以为特定的模块或组件注册提供商。要注册提供商,就要在服务的 @Injectable() 装饰器中提供它的元数据,或者在 @NgModule()@Component() 的元数据中。

默认情况下,Angular CLI 的 ng generate service 命令会在 @Injectable() 装饰器中提供元数据来把它注册到根注入器中。

@Injectable({
    providedIn: 'root'
})

依赖注入

DI 被融入 Angular 框架中,用于在任何地方给新建的组件提供服务或所需的其它东西。 组件是服务的消费者,也就是说,你可以把一个服务注入到组件中,让组件类得以访问该服务类。

依赖注入【DI】 可以保持组件类的精简和高效。有了DI,组件就不用从服务器获取数据、验证用户输入或直接把日志写到控制台,而是把这些任务委托给服务。

路由

Angular 的 Router 模块提供了一个服务,它可以让你定义在应用的各个不同状态和视图层次结构之间导航时要使用的路径。 它的工作模型基于人们熟知的浏览器导航约定:

  • 在地址栏输入 URL,浏览器就会导航到相应的页面。
  • 在页面中点击链接,浏览器就会导航到一个新页面。
  • 点击浏览器的前进和后退按钮,浏览器就会在你的浏览历史中向前或向后导航。

不过路由器会把类似 URL 的路径映射到视图而不是页面。 当用户执行一个动作时(比如点击链接),本应该在浏览器中加载一个新页面,但是路由器拦截了浏览器的这个行为,并显示或隐藏一个视图层次结构。

如果路由器认为当前的应用状态需要某些特定的功能,而定义此功能的模块尚未加载,路由器就会按需 惰性加载 此模块。

组件、视图、服务

overview

生命周期

每个组件都有一个被Angular管理的生命周期。

组件生命周期

指令和组件的实例有一个生命周期:当Angular新建、更新和销毁它们时触发。通过实现一个或多个Angular Core 库里定义的 生命周期钩子 接口,我们可以在特定时期执行特定方法。

生命周期顺序

当 Angular 使用构造函数新建一个组件或指令后,就会按下面的顺序在特定时刻调用这些生命周期钩子方法:

钩子 用途及时机
ngOnChanges() 当 Angular(重新)设置数据绑定输入属性时响应。 该方法接受当前和上一属性值的 SimpleChanges对象在 ngOnInit() 之前以及所绑定的一个或多个输入属性的值发生变化时都会调用。
ngOnInit() 在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。在第一轮 ngOnChanges() 完成之后调用,只调用一次
ngDoCheck() 检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。在每个变更检测周期中,紧跟在 ngOnChanges()ngOnInit() 后面调用。
ngAfterContentInit() 没当 Angular 把外部内容投影进组件/指令的视图之后调用。第一次 ngDoCheck() 之后调用,只调用一次。
ngAfterContentChecked() 每当 Angular 完成被投影组件内容的变更检测之后调用。ngAfterContentInit() 和每次 ngDoCheck() 之后调用
ngAfterViewInit() 每当 Angular 初始化完组件视图及其子视图之后调用。第一次 ngAfterContentChecked() 之后调用,只调用一次。
ngAfterViewChecked() 每当 Angular 做完组件视图和子视图的变更检测之后调用。ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。
ngOnDestroy() 没当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。在 Angular 销毁指令/组件之前调用。