延遲檢視
@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 才會被載入,而不是在應用初始化時全部載入。
存在的問題
- 元件延遲載入:比如頁面上有一個複雜的元件,想要當使用者交戶的時候再進行載入
- 部分模組載入:如果模組中有些片段需要根據條件或事件動態載入
@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,就進行載入當前元件
不滿足條件走@placeholder
name = 'angular';
@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);
}
到這裡簡單的用法已經介紹完成
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>
}
或者,您可以在與@defer塊相同的模板中指定一個模板引用變數,作為被監視以進入視口的元素,點選後進行觸發
<div #greeting>on 用法案例</div>
@defer (on interaction(greeting)) {
<app-choice-question></app-choice-question>
}
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>
}
在此就接介紹這幾種使用方法, 其他用法可以參考官網的寫法
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},
]});
兩者的區別
withInterceptors | withInterceptorsFromDi |
---|---|
函式式攔截器 | 類式攔截器(透過依賴注入管理) |
傳遞一個或多個攔截器函式 | 從依賴注入(DI)容器中載入攔截器類 |
適合簡單、快速實現的需求 | 適合複雜的業務邏輯或依賴注入的情況,如需要使用 Angular 服務時 |
總結
總體來說變好還是挺大的,看的出angular已經想要逐步移除模組,很多寫法都已經進行了變更,不像17之前的版本,總體變更不是很大,但是到了17之後語法大部分已經變更,這需要進行重新學習,也不能使用歷史的開發規範來使用當前版本的用法,其他新語法還需要多看官網進行學習。
https://v18.angular.dev/