写点什么

Angular 控制流与延迟视图揭秘

作者:PingCode
  • 2023-12-27
    江西
  • 本文字数:3192 字

    阅读完需:约 10 分钟

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}  



{#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

新的条件控制使用  @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  块支持接收两个参数  after  和  minimum 





@error

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





触发器 Triggers

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

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


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  滚动)

引用

用户头像

PingCode

关注

还未添加个人签名 2020-09-24 加入

PingCode 是简单易用的新一代研发管理平台,让研发管理自动化、数据化、智能化,帮助企业提升研发效能。

评论

发布
暂无评论
Angular 控制流与延迟视图揭秘_研发_PingCode_InfoQ写作社区