Angular 学习之路 03 - 代码结构

上一章我们介绍了 Angular CLI 为我们生成的那些除了源代码的项目相关文件。这一章我们将简单介绍源代码文件。

组件

app.component.ts 是 Angular CLI 为我们生成的组件文件,位于 src/app 目录下,其内容为:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'demo';
}

组件是我们应用中最重要的部分。它们代表了应用的视图。所谓视图,就是屏幕上可以看到的区域。以 app.component.ts 为例,这个文件通常包含三个部分:一个,一个类修饰器,以及这个类和类修饰器所需要的import语句

下面我们从头开始。

import语句

import { Component } from '@angular/core';

import语句用于向组件类引入依赖库。这个语句与 Java 的import类似。组件类使用@Component修饰器修饰,这个修饰器在 @angular/core 模块中定义,因此我们需要将其引入我们的类,也就是这里的import语句。

组件类

export class AppComponent {
  title = 'demo';
}

我们的组件AppComponent是一个普通的类。这个类使用export修饰,表示我们允许别的类使用import语句将其引入。这个类只有一个属性title。当应用运行时,title所代表的标题就会被显示出来(至于如何被显示,我们会在下面的内容详细说明)。

组件类可以有许多方法和属性,其主要目的是为视图提供背后的逻辑。

@Component修饰器

前面说过,AppComponent就是一个普通的类,那么,Angular 如何知道它是一个“代表组件的类”呢?答案就是通过@Component修饰器。@Component修饰器是一个类修饰器,提供了有关组件的元数据。这些元数据用于告诉 Angular 有关组件的一切,比如,怎样渲染组件等。Angular 使用这些元数据创建视图。

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

@Component修饰器可以包含很多信息,但这里仅使用了三个:

templateUrl

templateUrl指向一个 HTML 模板文件。Angular 将使用这个 HTML 文件渲染视图。在这个例子中,templateUrl指向 app.component.html 文件。当应用运行时,Angular 会使用这个 HTML 文件渲染这个组件。

styleUrls

styleUrls是一个样式表文件数组。Angular 将使用这些文件渲染 HTML 文件的样式。在这个例子中,styleUrls指向 app.component.css 文件;并且这个文件与AppComponent位于同一目录。

注意,这里我们说是样式表文件,没有说 CSS 文件,是因为 Angular 也支持使用 LESS 或者 SCSS 这样的 CSS 预处理文件。

selector

selector告诉 Angular 把模板显示在哪里。在这个例子中,selectorapp-root。当页面中出现了<app-root></app-root>标签时,Angular 会使用AppComponent替换,也就是将 app.component.html 的内容经过处理之后插入到相应的位置。selector使用 CSS 选择器的语法,因此如果我们写app-root,那么在 HTML 中就需要使用<app-root></app-root>;如果我们写.app-root,那么在 HTML 就可以使用<div class="app-root"></div>

模板

AppComponent组件模板文件是 app.component.html。前面我们说过,这是使用@Component修饰器的templateUrl指定的。

app.component.html 文件就在AppComponent的相同目录中,有 500 多行代码,基本都是标准的 HTML 片段和 CSS。

唯一不是标准的是有一个奇怪的{{ title }}语句(使用文本编辑器即可检索到)。双大括号告诉 Angular,从这个模板所在的类中取出这个属性放在这。app.component.html 对应的类是AppComponent,那么就需要在AppComponent类中找到title属性,然后把这个属性的值替换掉{{ title }}语句。所以当应用运行时,{{ title }}变成了title的值,即 demo。我们把这一替换的过程称为数据绑定

根模块

Angular 将应用代码以 Angular 模块的形式组织起来。模块是按照功能划分的一系列相关代码的集合,与 Java 的包相似。每个 Angular 项目都必须有至少一个模块。这个模块是应用运行时第一个被加载的,称为根模块。Angular 建议将根模块命名为AppModule。按照 Angular 的命名规范,AppModule所在文件为 app.module.ts,其内容如下:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Angular 模块的结构与组件类似,也包含三个部分:类、类修饰器以及import语句。

模块类

export class AppModule { }

与组件类类似,模块类也使用了export修饰,表示我们允许在其它模块使用这个模块。

@NgModule修饰器

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})

我们使用@component定义组件,使用@NgModule定义 Angular 模块。@NgModule将告诉 Angular 有关模块的元数据。AppModule@NgModule修饰器包含四个部分:

imports

imports语句定义这个模块引入了哪些模块。在这个例子中,AppModule引入了BrowserModule模块。一旦引入了某个模块,就可以使用这个模块中导出的组件、服务等。

declarations

declarations声明了这个模块所拥有的组件、指令、管道等。一个组件只能隶属于一个模块。declarations仅仅是声明,其它模块并不能够使用声明的那些组件,除非这个组件被导出

providers

providers声明了这个模块所能提供的服务。这些服务可以被其它组件使用。

bootstrap

bootstrap指定模块加载时,哪些组件需要一起加载。这里我们指定AppComponent。当加载AppModule时,Angular 会去找AppComponent并加载。

import语句

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

import语句导入AppModule所需要的 Angular 模块,例如BrowserModule以及NgModule。由于我们还使用了AppComponent,所以还需要把AppComponent导入。

注意:为什么有两个 import?文件最上方的import语句以及@NgModule修饰器中的imports定义?这是因为,文件最上方的import语句是 ES6 的模块导入,用于文件级别的相互引入,而@NgModule修饰器中的imports定义则是 Angular 模块之间的相互导入。一个 Angular 模块可以包含若干 ES6 模块。可以认为,ES6 模块是文件级的模块,Angular 模块是功能级的模块。

启动根模块

现在我们有了对这些文件的基本认识:app.component.html 是我们想给用户看的页面,它被绑定到AppComponent组件。我们要求AppModule加载时,就启动AppComponent组件。

现在,我们要告诉 Angular 在应用启动时,加载AppModule模块。这一步骤是在 main.ts 中进行的。main.ts 定义如下:

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
 
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
 
if (environment.production) {
  enableProdMode();
}
 
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

import语句的第一行从 @angular/core 库导入了enableProdMode。Angular 默认运行在开发模式。开发模式会运行许多额外的断言和检查,用于 debug 应用。enableProdMode指明当前 Angular 的构建环境是用于生产环境。

第二行导入platformBrowserDynamic库。这个库包含了启动 Angular 应用的全部功能。

当然,我们还得导入AppModule模块。

最后一个导入的是环境变量,是在 src/environments 目录中的。environment.ts 文件包含当前环境的变量。开发环境使用 environment.ts 文件。当使用生产环境构建项目时,会使用 environment.prod.ts 文件。这一替换是在 angular.json 中配置的。

接下来我们检查环境变量,如果是生产环境,那么就启用生产环境模式。

if (environment.production) {
  enableProdMode();
}

最后,platformBrowserDynamic库的bootstrapModule方法负责启动AppModule

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

index.html

index.html是我们的应用的入口,其内容如下:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Demo</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>
</body>
</html>

app-root选择器就是我们前面说过的AppComponent定义的选择器。正如我们前面所说,Angular 会检测 HTML 页面,如果找到<app-root></app-root>,则将其使用 app.component.html 的内容进行替换。

就这样,最终结果被显示到了屏幕上。我们也借此机会了解到 Angular 的最简单的一个完整结构。

2 Comments

  1. Mr.Bean 2019年10月6日
  2. langyo 2019年12月5日

Leave a Reply