angular 新語法糖學習一

kexb發表於2024-12-11

延遲檢視

@defer 是 Angular v16 引入的一項功能,用於最佳化元件的載入和渲染。它提供了一種延遲載入元件的方法,可以減少初始渲染時的開銷,延遲載入(Lazy Loading)主要是透過路由實現的。透過路由配置中的 loadChildren 屬性,可以在需要時載入特定的模組。這種方式適合處理大型應用程式中的模組劃分問題,但不能直接用於元件級別的延遲載入。隨著angular的發展,在angular v16開始引入了@defer,延遲載入不僅可以用於路由模組。也可以使用者元件和模版內容。

傳統的懶載入(Routing 模組懶載入)

懶載入透過 AppRoutingModule 配置,使用 loadChildren 延遲載入模組。

配置路由懶載入

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
    path: 'dashboard',
    loadChildren: () =>
      import('./dashboard/dashboard.module').then(m => m.DashboardModule),
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

Dashboard模組

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {DashboardComponent} from './dashboard.component';

const routes: Routes = [
  {
    path: '',
    component: DashboardComponent
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class DashboardModule { }

當前使用者訪問/dashboard路徑的時候,DashboardModule 才會被載入,而不是在應用初始化時全部載入。

存在的問題

  1. 元件延遲載入:比如頁面上有一個複雜的元件,想要當使用者交戶的時候再進行載入
  2. 部分模組載入:如果模組中有些片段需要根據條件或事件動態載入

@defer 的出現

Angular v16引入@defer增加延遲載入的場景,使得元件、管道、指令都可以進行按需載入

簡單示例

ChoiceQuestionComponent 元件

import {Component, OnInit} from '@angular/core';
import {CommonModule} from "@angular/common";

@Component({
  selector: 'app-choice-question',
  template: '<div class="card-shadow">
              <p>choice-question.component被載入成功</p>
            </div>',
  standalone: true,
  styleUrls: ['./choice-question.component.css']
})
export class ChoiceQuestionComponent {
}

AppComponent元件

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterModule,ChoiceQuestionComponent],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})

export class AppComponent {
  title = 'h5';
  name = 'yunzhi';
}
@defer (when name === "yunzhi") {
  <app-choice-question></app-choice-question>
} @placeholder {
  <div>displayed until name is not equal to yunzhi</div>
} @loading(after 1ms; minimum 3s) {
  <div> Loading content...</div>
}

當name如何等於yunzhi,就進行載入當前元件

image.png

不滿足條件走@placeholder

name = 'angular';

image.png

@loading觸發條件,當前元件需要進行載入一段時間的時候可以使用@loading實現載入效果

@loading(after 1ms; minimum 3s) {
  <div> Loading content...</div>
}

after 1ms:
指定載入狀態(佔位符)在延遲多久後開始顯示。
1ms 表示如果載入內容未完成,佔位符會在 1 毫秒後顯示。
如果在 1ms 內內容載入完成,則不會顯示佔位符(因為載入速度足夠快)。

minimum 3s:
指定佔位符至少顯示的時間長度。
即使內容載入在 1 秒內完成,佔位符也會繼續顯示 至少 3 秒,以避免載入完成後介面快速跳轉而影響使用者體驗。

ChoiceQuestionComponent 元件

設定setTimeout,10秒過後把dataLoaded設定為true,渲染檢視

<div @ngif="dataLoaded" class="card-shadow">
   <p>choice-question.component被載入成功</p>
</div>

export class ChoiceQuestionComponent implements OnInit {
  dataLoaded = false;

  ngOnInit() {
    setTimeout(() => {
      this.dataLoaded = true;
    }, 10000);
  }

image.png

到這裡簡單的用法已經介紹完成

On指定觸發@defer塊的條件。

觸發器描述
idle在瀏覽器空閒時觸發。
viewport當指定的內容進入視口時觸發。
interaction當使用者與指定元素互動時觸發。
hover當滑鼠懸停在指定區域時觸發。
immediate在非延遲內容渲染完成後立即觸發。
timer在指定的時間間隔後觸發。

1.idle

on idle:表示在瀏覽器空閒時才載入

@defer (on idle) {
  <app-choice-question></app-choice-question>
} @placeholder {
  <div>on 用法案例</div>
}

實際使用場景

適合延遲載入大型元件,是一個佔用較多資源的元件,可以使用 @defer 在空閒時載入。

2.interaction

點選佔位符進行觸發

@defer (on interaction) {
  <app-choice-question></app-choice-question>
} @placeholder {
  <div>on 用法案例</div>
}

image.png

image.png

或者,您可以在與@defer塊相同的模板中指定一個模板引用變數,作為被監視以進入視口的元素,點選後進行觸發

<div #greeting>on 用法案例</div>
@defer (on interaction(greeting)) {
  <app-choice-question></app-choice-question>
}

image.png

3.hover

當滑鼠懸停在指定區域時觸發

// 用法一
@defer (on hover) {
  <app-choice-question></app-choice-question>
} @placeholder {
  <div>on 用法案例</div>
}

或者採用模板引用變數的用法

// 用法二

<div #greeting>on 用法案例</div>
@defer (on hover(greeting)) {
  <app-choice-question></app-choice-question>
} 

image.png

在此就接介紹這幾種使用方法, 其他用法可以參考官網的寫法

https://v18.angular.dev/guide/templates/defer#

棄用HttpClientModule

自從 Angular 14 版本引入獨立元件以來,模組在 Angular 中已經變得可選。現在我們看到了第一個被棄用的模組:HttpClientModule。

以前用法

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

現在的用法

HttpClient是使用provideHttpClient helper函式提供的,目前app.config.ts中包含了這個函式。

bootstrapApplication(AppComponent, {
  providers: [
    provideHttpClient()
  ]
});

如果使用ngModule的方式定義

@NgModule({
  providers: [
    provideHttpClient(),
  ],
})
export class AppModule {}

支援配置HttpClient請求功能

provideHttpClient可以用於配置和提供 HTTP 客戶端服務

withFetch 功能

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(
      withFetch(),
    ),
  ]
};

在預設情況下,Angular 的 HttpClient 使用的是 XMLHttpRequest API 來發起 HTTP 請求,現在可以透過設定withFetch配置,切換尾fetch API的方式

配置 HTTP 客戶端攔截器的兩種方法

1. withInterceptors(...)

withInterceptors(...) 配置函式式攔截器的方式,這些攔截器將在透過 HttpClient 發出的請求中依次執行,函式接受請求物件 (HttpRequest) 和下一個處理函式 (HttpHandler),並返回請求或響應的修改,不需要引入額外的類和複雜的依賴注入

定義函式式攔截器

export function loggingInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {
  console.log(req.url);
  return next(req);
}

配置withInterceptors攔截器

bootstrapApplication(AppComponent, {providers: [
  provideHttpClient(
    withInterceptors([loggingInterceptor,cachingInterceptor]),
  )
]});

攔截器按照您在提供程式中列出的順序連結在一起。在上面的示例中,loggingInterceptor將處理請求,然後將其轉發到cachingInterceptor,也就是按照當前loggingInterceptor攔截器執行完,就執行cachingInterceptor攔截器

2.withInterceptorsFromDi() 基於DI注入的攔截器

withInterceptorsFromDi 使用 基於類的攔截器,這些攔截器是透過 Angular 的依賴注入(DI)機制進行管理的,每個攔截器類需要實現 HttpInterceptor 介面,並定義 intercept() 方法來處理請求和響應。

@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, handler: HttpHandler): Observable<HttpEvent<any>> {
    console.log('Request URL: ' + req.url);
    return handler.handle(req);
  }
}

基於withInterceptorsFromDi是透過依賴注入多提供商配置的:

bootstrapApplication(AppComponent, {providers: [
  provideHttpClient(
    withInterceptorsFromDi(),
  ),
  {provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true},
]});

兩者的區別

withInterceptorswithInterceptorsFromDi
函式式攔截器類式攔截器(透過依賴注入管理)
傳遞一個或多個攔截器函式從依賴注入(DI)容器中載入攔截器類
適合簡單、快速實現的需求適合複雜的業務邏輯或依賴注入的情況,如需要使用 Angular 服務時

總結

總體來說變好還是挺大的,看的出angular已經想要逐步移除模組,很多寫法都已經進行了變更,不像17之前的版本,總體變更不是很大,但是到了17之後語法大部分已經變更,這需要進行重新學習,也不能使用歷史的開發規範來使用當前版本的用法,其他新語法還需要多看官網進行學習。

https://v18.angular.dev/

相關文章