欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > Angular 控制流与延迟视图揭秘

Angular 控制流与延迟视图揭秘

2024/10/24 19:53:53 来源:https://blog.csdn.net/m0_54944506/article/details/143025937  浏览:    关键词:Angular 控制流与延迟视图揭秘

2023年11月8日 Angular 团队发布了 Angular 17 开发预览版,在新的版本,Angular 添加了许多激动人心的特性,其中就包含新的控制流和延迟视图
新版 Angular 增加一个 Block的概念,
Block是模板中的一种新语法结构,控制流和延迟视图也是基于这种语法结构来实现的。

控制流

什么是控制流?

控制流是指程序中决定语句执行顺序的机制。它通过顺序、选择(条件判断)、循环等结构,使程序能够根据不同条件或规则执行不同的代码块,实现灵活的逻辑控制,代码中常见的 if-else  for 都属于控制流语法。

Angular 中的控制流

Angular 16 及之前的版本中的控制流是基于微语法和结构指令来实现的,比如:

<div *ngIf="condition; else elseBlock">Content to render when the condition is true.
</div>
<ng-template #elseBlock>Content to render when the condition is false.
</ng-template>


<ng-container [ngSwitch]="tab"><basic-info *ngSwitchCase="'basic'"></basic-info><attachment-list *ngSwitchCase="'attachment'"></attachment-list><version-list *ngSwitchCase="'version'"></version-list><invaild-tab *ngSwitchDefault></invaild-tab>
</ng-container>
<ng-container [ngSwitch]="tab"><ng-container *ngSwitchCase="'version'"><version-list></version-list></ng-container><ng-container *ngSwitchCase="'attachment'"> <attachment-list></attachment-list> </ng-container><ng-container *ngSwitchCase="'basic'"><basic-info></basic-info></ng-container><ng-container *ngSwitchDefault><invaild-tab></invaild-tab></ng-container>
</ng-container>


开发体验不好:

  • 不够直观简洁
  • 灵活度差
  • 微语法模型也不支持控制流语句的多个关联子模板,所以 Angular 无法解决 *ngIf 的情况 else 使用起来非常尴尬的问题
  • 独立组件需要引入

新的控制流

关注 Angular 的同学了解,Angular 一直在推动 Zoneless 和 Singals 的工作,Angular 中现有的基于 Zone 的控制流指令将无法在 Zoneless 的应用中运行的,在考虑修改现有指令以支持 Zoneless 应用时,Angular 团队决定不这样做,因为潜在的重大更改(需要兼容旧的应用程序)和代码复杂性增加。相反,他们选择引入一种新的内置控制流语法,该语法既支持无区域应用程序,又解决微语法长期存在的 DX(开发体验)问题。
语法
官方在 RFC 时候提出了 #-syntax 类似 HTML 的标签语法,如 {#if} 、 {:else} 和 {/if}

{#if cond.expr}Main case was true!
{:else if other.expr}Extra case was true!
{:else}False case!
{/if}


不过在 RFC 讨论阶段,大部分人更喜欢 @-syntax 的语法,最终 Angular 团队通过调查和研究发现,大部分人也都认可这个方案,最终确定了使用了 @-syntax 的语法

@if (cond.expr){Main case was true!
} @else {False case!
}


使用
IF-ELSE

<div class="status-label" *ngIf="status === 1; else invalid">正常</div>
<div class="status-label" *ngIf="status === 2; else invalid">进行中</div>
<div class="status-label" *ngIf="status === 3; else invalid">完成</div><ng-template #invalid><div class="status-label">无效状态</div>
</ng-template>


<div class="status-label">@if (status === 1) {正常} @else if (status === 2) {进行中} @else if (status === 3) {完成} @else {无效状态}
</div>@if (users$ | async; as users) {{{ users.length }}
}


Switch

<ng-container [ngSwitch]="tab"><basic-info *ngSwitchCase="'basic'"></basic-info><attachment-list *ngSwitchCase="'attachment'"></attachment-list><version-list *ngSwitchCase="'version'"></version-list><invaild-tab *ngSwitchDefault></invaild-tab>
</ng-container>

For Loop
默认的 trackBy



@Component({
template: `


  • <ng-container *ngIf=“users?.length > 0; else empty”>
    <li *ngFor=“let user of users; trackBy: trackFn; let i = index”>
    No.${{ i + 1 }} {{ user.name }}


<ng-template #empty>没有用户</ng-template>

`,
})
export class DemoComponent {
trackFn(index: number, item: Item) {
return item.id;
}
}

  • @for (user of users; track user.id; let i = $index, e = $even) {
  • No.${{ i + 1 }} {{ user.name }}
  • } @empty { 没有用户 }
  
  • @for (user of (users$ | async); track user.id; let i = $index, e = $even) {
  • No.${{ i + 1 }} {{ user.name }}
  • }

延迟视图

在 Angular 16 中如何实现延迟加载组件?


@Component({
template: <ng-container #viewContainer />,
})
export class OldLazyComponent implements AfterViewInit {
@ViewChild(‘viewContainer’, { read: ViewContainerRef })
viewContainerRef!: ViewContainerRef;

async ngAfterViewInit() {
const { DemoComponent } = await import(‘./demo/demo.component’);
const componentRef = this.viewContainerRef.createComponent(DemoComponent);
}
}

@defer
Angular 支持通过 Router 延迟加载应用程序的某些部分(延迟路由),单个组件的延迟加载可以通过 dynamic import() 和 ngComponentOutlet 实现,但这种方法可能很复杂且容易出错,因此 Angular 17 在核心框架中引入一种更符合人体工程学的延迟加载组件的方法 @defer , @defer 不仅仅支持延迟组件,同时也支持延迟加载指令和管道,随着新的 @defer 的引入,我们可以更细颗粒度的控制我们的加载资源,优化应用初始包的体积,提升用户加载的速度。
使用限制
 @defer 块中的组件必须是独立组件,非独立组件不支持延迟加载并且会立即加载,及时他被包裹在 @defer 块中
不能在 @defer 块以外引用这个组件,包括使用 @ViewChild 查询这个组件
使用
@defer
 @defer 块中的内容最初不会显示,当满足指定的触发器或条件并获取依赖项,块中的内容才会展示,默认情况下,当浏览器状态变为空闲状态(Idle)时,会触发 @defer 加载。


@defer {

}

@defer {
{{name | transterName}}
}

@placeholder
默认情况下, @defer 块在触发之前不会呈现任何内容,我们可以定义 @placeholder 可选的块,声明在触发延迟块之前要显示的内容,当延迟内容加载完成后, @placeholder 块中的内容会销毁,需要注意的是在 @placeholder 块中的内容永远都是立即记载的


@defer {

} @placeholder {

}

 @placeholder 块接受一个可选参数 minimum 来指定应显示此占位符的时间,单位支持 s 和 ms。 minimum 是为了防止延迟项加载过快导致内容闪烁。


@defer {

} @placeholder(minimum 500ms) {

}

@loading
 @loading 与 @placeholder 一样,也是是一个可选块,允许我们指定加载依赖期间需要展示的内容,与 @placeholder 类似,它也是立即加载的,它可以接收两个参数 after 和 minimum 


@defer {

}@loading((after 100ms; minimum 1s)) {

}

@error
 @error 比较好理解,我们可以在 @error 中指定依赖加载失败时候展示的内容,与 @placeholder 和 @loading , @error 块的内容也是立即加载的,并且也是可选的


@defer {

}@error{

组件加载错误
}  触发器 Triggers 默认情况下,当浏览器状态变为空闲状态(Idle)时,会触发 @defer 加载,不过我们也可以根据自己的需求指定其他的触发器,Angular 提供了两种触发方式  When 和  On  When 条件触发 指定一个条件,当满足这个条件时触发   @defer (when cond) { }  On on 支持指定以下几种内置的触发器  Tigger Desc Example on idle 浏览器闲时触发,利用浏览器的 requestIdleCallback 特性   @defer (on idle) { }  on immediate 应用渲染完后立即触发   @defer (on immediate) { }  on timer 指定一个时间间隔后触发   @defer (on timer(500ms)) { }  on viewport Placeholder 或指定元素进入可视区域后触发,利用的 IntersectionObserver 特性,组员保证 Placeholder 必须是一个DOM节点   @defer (on viewport) { }@placeholder {
Placeholder
}   

const componentFixture = TestBed.createComponent(ComponentA);
// Retrieve the list of all defer block fixtures and get the first block.
const deferBlockFixture = componentFixture.getDeferBlocks()[0];

const deferBlockFixture = componentFixture.getDeferBlocks()[0];
// Render loading state and verify rendered output.
await deferBlockFixture.render(DeferBlockState.Loading);
expect(componentFixture.nativeElement.innerHTML).toContain(‘Loading’);

expect(componentFixture.nativeElement.innerHTML).toContain(‘加载’);
// Render final state and verify the output.
await deferBlockFixture.render(DeferBlockState.Completed);
expect(componentFixture.nativeElement.innerHTML).toContain(‘’);
});


插件支持
Prettier


npm i prettier@3.1 --save-dev


QA
支持自定义 Block 吗?
 @defer 可以定义自己的 on 的触发器吗?
控制流会支持自动迁移吗?
现有结构指令会废弃吗?
新的控制流会不会影响 ViewChild 查询结果?
新的控制流有更好的性能?
CDK 的虚拟滚动会收到影响吗?
CDK 目前将继续为虚拟滚动和其他用例提供其现有的结构指令。Angular 将研究将 CDK 的一些结构指令转换为内置语法,或者将扩展点添加到现有语法中供 CDK 构建(例如,支持虚拟 for 滚动)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com