Angular 開發學習 04 – 元件和模組
獲取本章的原始碼,可以使用 git clone https://gitee.com/devbean/learning-angular-todomvc.git
,然後檢出 cp04
標籤: git checkout cp04
即可。
我們從最開始的 ng new todomvc
開始說起。
這個命令是使用 Angular CLI 建立 Angular 專案的語句。它的執行結果是生成一個完整的 Angular 專案開發框架。對於簡單的應用,我們完全不需要考慮 webpack 的配置、 測試伺服器的熱部署等環境搭建的細節,就可以得到一個完善的開發環境。專案建立語句還可以有很多引數,比如,我們想使用 SCSS 而不是原始的 CSS,那麼就可以使用下面的語句:
ng new todomvc --style=scss
這樣,我們基本零配置就可以在開發時使用 SCSS。不過為簡單起見,我們暫時不會使用 SCSS,而是直接使用 CSS 作為 TodoMVC 的樣式表語言。
正如前面的章節說過的那樣,使用 ng serve
成功之後即可開啟瀏覽器檢視頁面的顯示。而我們也是介紹過, ng new
命令到底生成了什麼。
下面,我們從 src 中的 app 資料夾開始。app 資料夾是所有專案原始碼檔案所在地。以後的 ng 命令為我們生成的所有檔案都會在這個資料夾中。這也是我們主要關心的資料夾之一。
剛剛通過 ng new
命令獲得的 app 資料夾中有五個檔案:
- app.component.css
- app.component.html
- app.component.ts
- app.component.spec.ts
- app.module.ts
可以看到,這五個檔案分為兩類,分別為 component 和 module。
這種命名方式是 Angular CLI 推薦的。檔名分為三部分:第一部分是檔案的名字,第二部分是檔案的型別,第三部分是檔案的字尾名。例如,app.component.css,這個檔案隸屬於 app,這個 app 是一個 component,這個檔案字尾名是 css。
同時還可以看出,一種型別的檔案可能包含多個檔案。例如這裡的 component 就包含了四個檔案。我們會在後面詳細介紹這四個檔案。
component 和 module 是 Angular 中兩類最重要的型別。顧名思義,component 即 元件 ,module 即 模組 。二者的關係是,元件必然隸屬於一個模組,一個模組可以包含多個元件。
與 React 或者 Vue 類似,Angular 把頁面上所有的元素都看作元件。一個元件可以看作是一段可複用的 HTML 程式碼片段。在我們做 Java 或者 C++ 開發中,一段可複用的程式碼通常會抽象為一個函式。函式可以被其它函式呼叫。在 Angular 中也是類似,一段 HTML 程式碼片段也可能被複用,例如下面的程式碼:
<div class="portlet"> <div class="portlet-header"> Header </div> <div class="portlet-body"> ... </div> </div>
這段程式碼是一個帶有標題的 portlet 區域,大致類似下面的樣式:

這段 HTML 程式碼,連同為表現其樣式所附加的 CSS,甚至還有其行為的 JavaScript,放在一起即可被另外的頁面複用。那麼,我們就可以把這些程式碼放在一起,抽象為一個元件。在使用 Angular 開發頁面時,很多時候都是在把頁面分解成為一個個獨立的元件,最終將這個元件專案組合起來,完成最終頁面的開發。
在 Angular 中,每一個元件都必須屬於一個模組。可以認為模組是元件的集合(事實上,模組不僅僅包含元件,還可以有其它內容)。同一模組中的元件可以直接相互使用;不同模組中的元件在相互使用時,需要匯入元件所在模組。也就是說,如果你想要使用別人提供的元件,應該引入該元件所在的模組,而不是元件本身。這暗示著,模組其實是 Angular 最基本的部件,因為 Angular 的專案是通過相互引用模組,錯綜複雜地耦合在一起。
在 Angular 中,至少有一個模組作為 根模組 ;根模組中至少有一個元件作為 根元件 。這個根模組一般命名為 AppModule
;這個根元件一般命名為 AppComponent
。按照前面所說 Angular 的命名規則, AppModule
檔名為 app.module.ts; AppComponent
檔名為 app.component.ts。這就是 Angular CLI 為我們生成的幾個檔案的來歷。
瞭解到這一點,下面我們開啟 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 { }
字尾名 ts 意味著 Angular 的專案使用 TypeScript 編寫。Angular 提供多種語言開發:ES6,Dart,TypeScript。但官方推薦的是使用 TypeScript。幾乎所有 Angular 教程都是使用 TypeScript。因此,我們也選擇使用 TypeScript 進行學習。在一定程度上說,這是必須的,因為雖然 Angular 提供了多種語言的開發方式,但 Angular 本身就是用 TypeScript 開發的。
TypeScript 是 JavaScript 的超集。任何合法的 JavaScript 都是合法的 TypeScript。TypeScript 提供了比 JavaScript 更多的功能,比如完整的型別系統、修飾器等。有過 Java 或者 C# 開發經驗的開發者很容易上手 TypeScript。這裡我們不會詳細介紹 TypeScript,而是在使用中慢慢接觸解釋有關 TypeScript 的相關內容。
TypeScript 有類的概念。因此,我們可以很明顯地看到程式碼最後一行 export class AppModule { }
,是如何將一個類匯出的。 export
關鍵字來自 ES6,意味著這個類 AppModule
可以在其它模組使用。注意,這裡所說的“模組”,不是 Angular 的模組,而是 ES6 系統中的模組。ES6 模組與 Angular 模組有本質區別。ES6 模組是語言級別的,每個 檔案 是一個模組,檔案中定義的所有物件都從屬於那個模組。 通過 export
關鍵字,模組可以把它的某些物件宣告為公共的。 其它 JavaScript 模組可以使用 import
語句訪問這些公共物件。
JavaScript,ES,ES6 和 ES2015
ES 是 ECMAScript 的縮寫。ECMAScript 是一個標準,由 ECMA 國際(前身為歐洲計算機制造商協會,英文名稱 European Computer Manufacturers Association)通過 ECMA-262 標準化的指令碼程式設計語言。JavaScript 是 ECMAScript 的一種實現。ECMAScript 還有其它實現,比如 ActionScript。 2015年6月,ECMAScript 6.0(ES6)正式釋出,也就是 JavaScript 語言的下一代標準。ECMA 國際為了更頻繁地釋出包含小規模增量更新的新版本,將於 2016 年釋出的新標準命名為 ECMAScript 2016。之後的新版本將按照 ECMAScript + 年份的形式命名釋出。因此,按照這個規定,於 2015 年釋出的 ES6 被重新命名為 ES2015。因此,ES6 和 ES 2015 僅是同一標準的不同稱呼而已。 Angular 自帶了一組 JavaScript 模組,可以把它們看成庫模組。每個 Angular 庫的名稱都帶有 @angular
字首。 使用 JavaScript 的 import
語句匯入其中的各個部分。比如,
import { BrowserModule } from '@angular/platform-browser';
就是從 JavaScript 模組 @angular/platform-browser
匯入了 Angular 模組 BrowserModule
。
我們希望把 TypeScript 的類作為 Angular 的模組或者元件的基礎。這是很自然的想法:面向物件設計思想告訴我們,模組或者元件天然就是一個物件。那麼,如果確定一個類是模組還是元件,還是其它什麼東西呢?Angular 選擇使用裝飾器來標明。裝飾器( Decorator )這個名詞來自 Python,在 Java 中被稱為“註解 Annotation”,在 C# 中被稱為“屬性 Attribute”。
為什麼 AppModule
是一個 Angular 模組呢?因為它使用了 @NgModule
裝飾器。被 @NgModule
裝飾器裝飾的類,Angular 就認為是一個模組。而這個裝飾器,也是從一個 JavaScript 模組中匯入的:
import { NgModule } from '@angular/core';
除了 @NgModule
,Angular 還提供了很多其它的裝飾器。我們會在後面詳細介紹這些裝飾器。
@NgModule
裝飾器接受一個 JSON 物件作為引數:
@NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
declarations
表示這個模組中 宣告 了哪些元件。前面說過,元件必須隸屬於一個模組,也就是說,一個元件必須在某一個模組進行宣告。並且,一個元件只能在一個模組中進行宣告。上面的程式碼中,AppModule 聲明瞭 AppComponent 元件。需要指出的是, declarations
中不僅僅是元件,還可能有其它元素,比如指令等。我們會在後面再介紹這部分內容。
imports
表示這個模組 匯入 了哪些模組。前面說過,模組之間可以相互引用。引用的方式就是匯入其它模組。這裡,AppModule 匯入了 BrowserModule,因此就可以使用 BrowserModule 中公開的元件或其它元素。
providers
表示這個模組 提供 了哪些服務。服務是一般作為邏輯的抽象。元件通過服務實現自己的業務邏輯。比如,有的服務提供了 HTTP 請求,有的服務提供了使用者狀態管理等。
bootstrap
表示這個模組 啟動 哪些元件。應用程式在啟動時會引導根模組 AppModule
,將其作為一個入口元件 entryComponent
。引導中會建立所有列出在 bootstrap
中的元件,然後將其插入 DOM,形成一棵元件樹。雖然 bootstrap
是一個數組,但一般一個應用程式只有一棵元件樹,也就是隻有一個引導元件。這個元件通常被稱為 AppComponent。
AppComponent 是 Angular CLI 生成的另外一個重要的部分。與 AppModule 不同的是,AppComponent 由四個檔案構成,這四個檔案具有類似的命名:通常以 component 作為中間名,字尾名不同。這與 app.module.ts 是一致的。組成 AppComponent 的四個檔案分別是:
- app.component.ts:AppComponent 的類所在檔案
- app.component.spec.ts:AppComponent 單元測試程式碼檔案
- app.component.html:AppComponent 的 HTML 渲染檔案
- app.component.css:AppComponent 的樣式檔案
我們從 app.component.ts 開始看起:
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'todomvc'; }
與 AppModule
類似, AppComponent
同樣只是一個普通的 TypeScript 類。區別在於,後者使用 @Component
裝飾器修飾。顧名思義, @Component
用於標識一個類是一個元件。這個裝飾器,同樣是從 JavaScript 模組 @angular/core 匯入的。 @Component
裝飾器也接受一個 JSON 物件作為引數。在 AppComponent
中, @Component
使用了下面幾個引數:
-
selector
:字串型別的選擇器,其使用與 CSS 選擇器類似。例如,這裡設定selector: 'app-root'
,那麼,我們在 HTML 中使用<app-root></app-root>
即可使用這個元件。也就是說,我們建立了一個新的 HTML 標籤。同樣,我們也可以使用selector: '[appRoot]'
,注意[appRoot]
其實就是 CSS 的屬性選擇器的語法,類似的,我們就需要使用<div appRoot></div>
來使用這個元件。一般而言,我們通常會將元件作為新的 HTML 標籤,但某些情況下則需要使用另外的技巧。我們會在實際應用中看到這些內容。 -
templateUrl
:模板 URL。如果我們把元件看作新的 HTML 標籤,瀏覽器在遇到這個新的標籤時,需要知道要如何渲染這個標籤,也就是這個元件的模板。Angular 使用templateUrl
指定元件模板檔案的位置。 -
styleUrls
:樣式表 URL。templateUrl
定義了元件模板檔案的位置,模板檔案確定了元件的結構,元件的樣式則需要使用 CSS 檔案定義,也就是這裡的styleUrls
。注意,styleUrls
的型別是一個數組,因此,一個元件可以有多個樣式表文件。
回到 app.component.ts,我們通過 @Component
的引數就可以知道,AppComponent 的選擇器是 app-root
,其模板檔案路徑是 ./app.component.html,其樣式表文件路徑是 ./app.component.css。後兩者都是通過相對路徑指定了檔案的位置。也就是說,app.component.html 和 app.component.css 與 app.component.ts 是同目錄的。
Angular CLI 為我們生成的 AppComponent 類只有一個屬性: title
,其值被設定為 todomvc。
app.component.html 的內容則是:
<!--The content below is only a placeholder and can be replaced.--> <div style="text-align:center"> <h1> Welcome to {{ title }}! </h1> <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="> </div> <h2>Here are some links to help you start: </h2> <ul> <li> <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2> </li> <li> <h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2> </li> <li> <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2> </li> </ul>
這是一個普通的 HTML 片段,除了有一個特殊的 {{title}}
語法。這裡簡單說一下,這個語法意味著,這個位置的內容會被 繫結 到 AppComponent 類的 title
屬性。 {{title}}
的內容會被自動替換為 title
屬性的值。注意我們說的是“繫結”,這意味著,當 title
值改變時, {{title}}
的值會自動隨之改變。我們會在後面詳細介紹這部分內容。
app.component.css 的內容是空白的,也就是不新增任何特殊的樣式。
好了,現在模組和元件都準備好了,並且元件也被新增到了模組。前面我們說過,這樣定義好之後,使用 <app-root></app-root>
就可以使用 AppComponent
。這部分程式碼在哪裡呢?現在開啟 index.html,我們就會看到:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Todomvc</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>
Angular CLI 在這裡通過 <app-root></app-root>
使用了 AppComponent
元件。因此,我們能夠在螢幕上看到瀏覽器渲染出來的頁面。
現在,我們大致瞭解 Angular CLI 生成的檔案,以及 Angular 中最重要的元件和模組的含義。之後的章節中,我們將在此基礎上,逐步實現 TodoMVC 應用所要求的各種功能。