[從 0 開始的 Angular 生活]No.7 Angular 應用程式的啟動過程

前言

當我們透過 VSCode 的終端機執行 「npm start」 時,這段時間發生了什麼事情,讓我們好好地整理一下。

npm start

首先開啟一個之前就建立好的 Angular 專案範本,接著在 VSCode 的終端機執行 npm start 。

透過這張圖可以觀察到,當執行 npm start 時,其實是呼叫執行 Angular CLI 的命令 ng serve ,這個過程會啟動一個開發伺服器,而這個開發伺服器在啟動之前,背後透過 Webpack 將目前的 Source Code 內所有的 TypeScript 進行編譯。

編譯之後把所有的 JavaScript 檔案合併在一起,而這個過程中產生了幾支檔案:

  • es2015-polyfills.js, es2015-polyfills.js.map
  • polyfills.js, polyfills.js.map
  • runtime.js, runtime.js.map
  • styles.js, styles.js.map
  • vendor.js, vendor.js.map

那麼這些檔案會用在什麼地方呢?

index.html

在首頁打開的時候,預設會把剛才編譯產生的這些檔案給載入,我們可以透過觀察原始碼來了解。

對著 index.html 按右鍵 > 檢視網頁原始碼

首先我們看到一個

標籤,這個標籤可能比較少用,詳細的應用可以到這邊查看。

接著是標籤,這個標籤事實上就是 Angular 裡面的根元件,也可以稱它為一個 directive ,關於 directive 之後會有更詳細的說明。

然後發現 之前被插入了剛剛編譯出來的 .js 檔,而 Angular 應用程式也在載入這些 .js 檔後正式開始運行。

然而執行的過程中,也有一個啟動的流程。

啟動的流程結束後,標籤的內容就會被動態的插入一些 DOM 物件,最後顯示在畫面上。

對著 index.html 按右鍵 > 檢查

從 chrome 的開發者工具底下,可以看到標籤包含了一些從剛剛的原始碼內看不到的標籤。

因為這些內容全部都是透過 Angular 應用程式動態運算出來的結果。

實際觀察專案內的 index.html 檔案

這支檔案也就是剛才 chrome 瀏覽器開啟的檔案,而裡面確實有個標籤。

可以發現檔案內的

標籤內並沒有插入剛才那些 .js 檔,也就是說那是 Webpack 幫我們編譯後動態插入的。

也就是說我們在開發 Angular 網頁時,它的 JavaScript 在開發時期是動態被注入的。

從哪支 .js 檔開始跑呢?

之前有提過, Angular 應用程式的進入點是 main.ts 檔案,而它的長相如下:

1
2
3
4
5
6
7
8
9
10
11
12
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));

前面 5 行主要是引用從某一些模組匯入程式運行時必要的物件進來。
而第 11 行的地方,則是透過 platformBrowserDynamic().bootstrapModule(AppModule) 去執行啟動模組這件事,接著會進入到 AppModule 裡面執行相關的程式碼,讓我們一起觀察下去吧。

小提示:在 VSCode 內可以對著 AppModule 點一下接著按 F12 會自動追蹤到該檔案喔

AppModule 內

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 應用程式裡面最重要的一支程式,而程式碼結構不難看出它是一個 class 而且被 export 出來,神奇的是裡面沒有任何的程式碼。

在大部分的情況,我們寫 Angular 應用程式時確實是不需要寫程式在裡面的。

我們只需要套用一個 declarator ,而這個 declarator 要設定成 NgModule ,去宣告這個類別它是一個 Angular 的 Module ,然後我們在這個 Module 裡面又有好幾個 Property (屬性) 需要宣告:

  • declarations - 用來宣告一些跟 view 有關的元件
  • imports - 用來匯入一些跟這個模組會用到的其他模組,而模組說穿了就是多個元件封裝後的東西
    • 而 BrowserModule 就是把 BrowserModule 內所有的元件一起匯入進來的意思
  • providers - 註冊服務的提供者
  • bootstrap - 啟動根元件 AppComponent ,也就是 Angular 最上層的元件 (預設名稱為 AppComponent)

接著我們使用剛才介紹的追蹤方式,繼續追蹤 AppComponent

AppComponent 內

1
2
3
4
5
6
7
8
9
10
import { Component } from '@angular/core';

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

AppComponent 元件的程式碼結構也一樣是個被 export 的 class ,然後這個範例程式預設有一個屬性 title 。

這個元件一樣有使用到 declarator ,叫做 Component ,這個 Component 在 Angular 內有特殊的涵義,這是用來宣告這個 class 代表的是一個 Component 。

而這個 Component 同樣有一些屬性如:

  • selector - 如果寫過 JQuery 肯定不會陌生,就是一個選取器,如同範例的 app-root 就是選取到 HTML 中 app-root 標籤,並且把這個標籤的內容,修改為這個元件執行的結果。
    • 同理,如果改寫成 .app-root 則是選取具有 .app-root 的 className

舉例來說可以這麼做,我們修改 AppComponent 與 index.html 如下:

1
2
3
4
5
6
7
8
9
10
import { Component } from '@angular/core';

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

1
2
3
4
<body>
<!-- <app-root class="app-root"></app-root> -->
<div class="app-root"></div>
</body>

之後回到瀏覽器觀察,發現仍然是可以運行的。

而 HTML 中不管是直接在元件寫上 className 或者是新建一個具有 app-root className 的 div 標籤都是有效果的。

在我們把 selector 修改成 .app-root 後,下方突然多出綠色的蚯蚓,這是因為我使用了 TSLint ,而 TSLint 這個套件是遵照 Angular 官方發布的 Style Guide 規範,這當中就包含了 Component 的 selector 最好都以 element 的 selector 為主。

  • templateUrl - 指這個 AppComponent 的 HTML Template 的所在之處,而這堆內容就是我們在瀏覽器上看到的 HTML 內容

所以我們的每一個 Component 都會有一個相對應的 HTML Template 做搭配,一個負責程式的邏輯、另一個負責呈現於瀏覽器。

  • styleUrls - 指 AppComponent 的 HTML Template 有用到的 CSS 樣式,需要注意的是這裡的樣式預設只針對這個 Component ,並不會與其他 Component 互相衝突,當然這個預設值也是可以調整的。

小結

以上就是 Angular 應用程式的啟動流程,透過這樣的描述能讓自己更加地了解 Angular 的運作過程。

0%