What is Standalone?

Angular 16 推出了 Standalone 的版本,

可以不用透過 NgModule 引用其他 standalone 的 Component,

在 Angular 16 之前,

要透過 module.ts 在其中定義方法來引用其他 Component:

// import 的程式碼都放在這個區塊
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";

import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";

// Decorator 裝飾器
@NgModule({
  declarations: [AppComponent], // 放入 Component, Directive, Pipe
  imports: [BrowserModule, AppRoutingModule], // 放入此 NgModule 需要使用 & 依賴的其他 NgModule
  providers: [], // 放入全域使用的 Service
  bootstrap: [AppComponent], // 決定進入點要使用哪一個 Component, 預設使用 AppComponent
})
export class AppModule {}

而新的 Standalone 版本則是沒有了 module.ts 等檔案,

多了 app.config.ts 來定義 providers,

如果一個 Component 需要用到另一個 Component,

可以直接在其中引用,

若要在 AppComponent 中引用 HeaderComponent 可以這樣寫:

import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { RouterOutlet } from "@angular/router";
import { HeaderComponent } from "./header/header.component"; // 引用其他 Component

@Component({
  selector: "app-root",
  standalone: true, // 這裡定義了這個 Component 為 Standalone
  imports: [CommonModule, HeaderComponent, RouterOutlet], // 這裡定義了這個 Component 需要使用 & 依賴的其他 Component
  templateUrl: "./app.component.html",
  styleUrl: "./app.component.css",
})
export class AppComponent {
  title = "test-for-standalone-1";
}

What is Component?

Component 負責 定義 & 控制畫面,

讓 Angular 可以根據資料和程式邏輯呈現相對應的畫面,

通常由四個檔案組成一個 Component:

  • .component.css => 用於定義該元件的 CSS 樣式

  • .component.html => 作為該元件範本的 HTML 檔案

  • .component.ts => 存放該元件類別 Component 的 TypeScript 檔案

  • .component.spec.ts => 用於單元測試

解析 app.component.ts:

// import 的程式碼都放在這個區塊
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { RouterOutlet } from "@angular/router";
import { HeaderComponent } from "./header/header.component"; // 引用其他 Component

// Decorator 裝飾器
@Component({
  selector: "app-root",
  standalone: true, // 這裡定義了這個 Component 為 Standalone
  imports: [CommonModule, HeaderComponent, RouterOutlet], // 這裡定義了這個 Component 需要使用 & 依賴的其他 Component
  templateUrl: "./app.component.html",
  styleUrl: "./app.component.css",
})
export class AppComponent {
  title = "test-for-standalone-1";
}

@Component:

  • selector => CSS 選擇器

    • 它告訴 Angular 在 Template 中找到相應的位置之後, 創建並插入該 Component 實體

    • 以上述為例, 在 HTML 中我們可以使用 <app-root></app-root> 來顯示該 Component 的內容

  • standalone => 定義此 Component 為 Standalone

  • templateUrl => 此 Component 的 Template 檔案位置(相對位置)

  • template => 與 templateUrl 的用途類似, 但這裡是直接放 HTML 的語法(不建議使用)

  • styleUrls => 樣式檔檔案位置(相對位置)

  • styles => 與 styleUrls 類似, 但這裡可以直接放 CSS 的語法(不建議使用)

  • providers => 與 NgModule 的 providers 類似, 但作用區域不同

What is Template?

Template 是由 HTML + CSS 再加上一些 Angular 提供的 符號 & 屬性 所組成

Template 上的 元素 & 資料 都是靜態的,

搭配 Component 裡的 TypeScript 即可控制 Template 中要顯示的內容.

Single Page Application

Angular 的架構是一個 SPA 單頁應用, 起始頁為 index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>TestForStandalone1</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>
    <!-- 插入 Component 在這裡 -->
    <app-root></app-root>
  </body>
</html>

在 index.html 中, 可以看到這裡定義了一些常見的 html 標籤,

可以注意到在 <body></body> 裡面, 放著 AppComponent 的選擇器標籤,

因此當進入 index.html 時, <body></body> 中的 <app-root></app-root> 會被替換成 AppComponent 內容,

AppComponent 的內容便是 templateUrl + styleUrls + ts 所組成的畫面

接著看到 angular.json:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "Test": {
      "projectType": "application",
      "schematics": {},
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/test",
            // 這裡定義了起始頁面
            "index": "src/index.html",
            // 這裡定義了起始的ts執行檔
            "main": "src/main.ts",
            "polyfills": ["zone.js"],
            "tsConfig": "tsconfig.app.json",
            "assets": ["src/favicon.ico", "src/assets"],
            "styles": ["src/styles.css"],
            "scripts": []
          },
.
.
.

在 angular.json 中:

  • Index: src/index.html => 決定 index.html 為起始頁面也是唯一頁面

  • main: src/main.ts => 決定 main.ts 為起始的 ts 執行檔

再來看到 main.ts:

// Angular 16
import { bootstrapApplication } from "@angular/platform-browser";
import { appConfig } from "./app/app.config";
import { AppComponent } from "./app/app.component";

bootstrapApplication(AppComponent, appConfig).catch((err) =>
  console.error(err)
);

main.ts 透過 bootstrapApplication 啟動 AppComponent 並傳入 appConfig.

而在 angular 16 之前的版本則是會透過 透過 bootstrapModule 啟動 AppModule:

// Angular 16 之前
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";

import { AppModule } from "./app/app.module";

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

運作流程差異:

  • Berfore Angular 16:

    • index.html => main.ts => app.module.ts => app.component.ts
  • After Angular 16:

    • index.html => main.ts => app.config.ts => app.component.ts

參考