延迟视图
@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之后语法大部分已经变更,这需要进行重新学习,也不能使用历史的开发规范来使用当前版本的用法,其他新语法还需要多看官网进行学习。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。