Angular 控制流与延迟视图揭秘

2023年11月8日 Angular 团队发布了 Angular 17 开发预览版,在新的版本,Angular 添加了许多激动人心的特性,其中就包含新的控制流和延迟视图

新版 Angular 增加一个 Block的概念, Block 是模板中的一种新语法结构,控制流和延迟视图也是基于这种语法结构来实现的。

控制流

什么是控制流?

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

Angular 中的控制流

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

问题

  • 不够直观简洁(目前模板中基于指令的控制流会使模板代码变的冗长,这种冗长会导致降低代码的可读性)
  • 灵活度差(微语法模型也不支持控制流语句的多个关联子模板,所以 Angular 无法解决 *ngIf 的情况 else 使用起来非常尴尬的问题)
  • 类型检查不友好
  • 独立组件需要引入

新的控制流

关注 Angular 的同学了解,Angular 一直在推动 Zoneless 和 Singals 的工作,Angular 目前的控制流指令是无法在 Zoneless 的应用中运行的,在考虑修改现有指令以支持 Zoneless 应用时,Angular 团队决定不这样做,因为潜在的重大更改(需要兼容旧的应用程序)和代码复杂性增加。相反,他们借此机会选择引入一种新的内置控制流语法,该语法既支持 Zoneless 应用程序,又解决微语法长期存在的 DX(开发体验)问题。

语法

官方在 RFC 时候提出了 #-syntax 类似 HTML 的标签语法,如 {#if} {:else}{/if}

js{#if 复制代码
  Main case was true!
{:else if other.expr}
  Extra case was true!
{:else}
  False case!
{/if}

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

js@if 复制代码
   Main case was true!
} @else {
   False case!
}

使用

IF-ELSE

新的条件控制使用 @if@else 块来定义,与旧的 `*ngIf` 语法相比,新的控制流同时还支持了 "else if" 的能力

新的语法依旧支持通过 async 订阅

Switch

新的 Switch 与 @switch @case @default 三个块组成,支持直接在块中传入内容,新的这种使用方式使我们的代码更清晰直观

For Loop

新的 for 循环我认为变化最大的,在语法上,新的 @for 简化了 `*ngFor的使用,不需要写let,同时直接直接指定track` 属性,在性能上,Angular 也做了比较大的优化,同时还内置支持了 empty 场景。 

新的语法依旧支持通过 async 订阅 

内置的隐藏变量依旧与 *ngFor 保持一致

延迟视图

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

@defer

Angular 支持通过 Router 延迟加载应用程序的某些部分(延迟路由),单个组件的延迟加载可以通过 dynamic import()ngComponentOutlet 实现,但这种方法可能很复杂且容易出错,因此 Angular 17 在核心框架中引入一种更符合人体工程学的延迟加载组件的方法 @defer@defer 不仅仅支持延迟组件,同时也支持延迟加载指令和管道,随着新的 @defer 的引入,我们可以更细颗粒度的控制我们的加载资源,优化应用初始包的体积,提升用户加载的速度。

使用限制

  • @defer 块中的组件必须是独立组件,非独立组件不支持延迟加载并且会立即加载,即使它被包裹在 @defer 块中
  • 不能在 @defer 块以外引用这个组件,包括使用 @ViewChild 查询这个组件

使用

@defer

@defer 块中的内容最初不会显示,当满足指定的触发器或条件并获取依赖项,块中的内容才会展示,默认情况下,当浏览器状态变为空闲状态(Idle)时,会触发 @defer 加载。 

@placeholder

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

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

@loading

@loading @placeholder 类似,也是是一个可选块, @loading@placeholder 区别是 @loading 块中指定的内容只有当资源加载时候才会展示,在资源未被加载之前永远展示的都是 @placeholder ,` @loading 块支持接收两个参数 afterminimum 

@error

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

触发器 Triggers

默认情况下,当浏览器状态变为空闲状态(Idle)时,会触发 @defer 加载,不过我们也可以根据自己的需求指定其他的触发器,Angular 提供了两种触发方式When On

When 条件触发

指定一个条件,当满足这个条件时触发   On 支持以下几种内置的触发器

on idle (浏览器闲时触发,利用浏览器的 requestIdleCallback 特性)   on immediate(应用渲染完后立即触发)   on timer(指定一个时间间隔后触发)   on viewport(Placeholder 或指定元素进入可视区域后触发,利用的 IntersectionObserver 特性,组员保证 Placeholder 必须是一个DOM节点)   on hover(Hover Placeholder 或者 Hover 指定某个区域触发)   on interaction(Placeholder 或者指定元素触发 click 或者 keydown 事件时触发)   On 和 When 可以组合使用 

预获取资源 Prefetch

@defer 允许指定何时触发依赖项的预取条件,我们可以使用 prefetch 关键字,prefetch 可以 when 和/或 on 结合声明触发器 

一些注意事项

  • @defer 块中如果不包含任何依赖项(组件、指令、管道)等,则这个 @defer 块是无效的
  • @defer 块与 ng-content 结合使用时,外部投影的内容不会延迟加载,如果要延迟加载投影的内容,可以在外部将投影的内容单独包装到 @defer 块中
  • @defer 支持嵌套,我可能指定进入可视区域时渲染一个大组件,然后用户Hover某个元素时渲染一个内部子组件,不过需要注意尽量避免这样的使用,可能会导致性能问题

编译结果

编译后, @defer 块中的组件会打包成一个一个 chunk 文件用于单独的加载


 ### 测试

Angular 提供 TestBed API 来简化测试块和在测试过程中触发不同状态的过程 @defer 。默认情况下, @defer 测试中的块是"暂停"的,我们可以手动在状态之间转换 

插件支持

Prettier

js 复制代码
npm i prettier@3.1 --save-dev

QA

Q:控制流会支持自动迁移吗?

A:Angular 将会提供 Schematics 工具自动迁移

Q:新的控制流会不会影响 ViewChild 查询结果?

A:不会,与原有的结构指令行为一致

Q:现有结构指令会废弃吗?

A:不会,结构指令是 Angular 中应用程序架构的一个基本功能,Angular 没有计划删除它们

Q:未来支持自定义 Block 吗?

A:暂时不会,未来可能会

Q:在 @defer 可以定义自己的 on 的触发器吗?

A:不可以,可以用 When 来实现自定义触发器

Q:CDK 的虚拟滚动会收到影响吗?

A:CDK 目前将继续为虚拟滚动和其他用例提供其现有的结构指令。Angular 将研究将 CDK 的一些结构指令转换为内置语法,或者将扩展点添加到现有语法中供 CDK 构建(例如,支持虚拟 for 滚动)

引用

相关推荐
勘察加熊人3 天前
andrular输入框input监听值传递
angular.js
勘察加熊人4 天前
Angular解析本地json文件
javascript·json·angular.js
勘察加熊人4 天前
angular实现list列表和翻页效果
javascript·angular.js
界面开发小八哥5 天前
界面控件Kendo UI for Angular 2024 Q3亮点 - 全新的页面模板
前端·ui·界面控件·kendo ui·angular.js·ui开发
勘察加熊人6 天前
angular登录按钮输入框监听
angular.js
勘察加熊人7 天前
angular使用http实现get和post请求
前端·http·angular.js
勘察加熊人7 天前
angular实现dialog弹窗
javascript·ecmascript·angular.js
勘察加熊人7 天前
angular实现calculate计算器
angular.js
无敌喜之郎7 天前
Angular中ChangeDetectorRef.detectChanges是如何实现的,对比vue种的nextTick有何不同
前端·vue.js·angular.js·视图同步
界面开发小八哥7 天前
界面控件DevExpress JS & ASP.NET Core v24.1亮点 - 支持Angular 18
javascript·ui·asp.net·界面控件·angular.js·devexpress