Angular 17+ 高级教程 – 盘点 Angular v14 到 v17 的重大改变

这篇具有很好参考价值的文章主要介绍了Angular 17+ 高级教程 – 盘点 Angular v14 到 v17 的重大改变。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

我在 <初识 Angular> 文章里有提到 Angular 目前的断层问题。

Angular 17+ 高级教程 – 盘点 Angular v14 到 v17 的重大改变

大部分的 Angular 用户都停留在 v9.0 版本。

Why everyone stay v9.0? 

v9.0 是一个里程碑版本,Angular 从 v4.0 稳定版推出后,好几年都没有什么动静,直到 v9.0 推出了 Ivy rendering engine。

本以为 v9.0 以后 Angular 会大爆发,结果迎来的是 Angular 团队搞内讧,又...好几年没有动静。直到 v14.0 Angular 突然就...变了🤔。

Angular 团队大换血之后,有了新方向,原本那批人的特色 “不爱创新,爱 follow 标准,爱小题大" 现在已不复存在,新一批人的特色是 "爱 follow 市场,爱新用户,爱借其它团队的力"。

这也是为什么从 v14 以后大家都感觉 Angular 好像不是 Angular 了😂。

是福是祸,现在还很难说,所以大部分人都宁愿停留在 v9.0 继续观望,反正 v9.0 到 v17 也没有推出什么新功能。

v14 到 v17 那么多改变,都离不开 "爱 follow 市场,爱新用户" 原则,所以老一批的用户看待这些改变的第一反应都是嗤之以鼻。

为什么写这篇?

很多人在静观其变,但同时心里又有些焦虑,本篇就是要带你体会一下 v14 后的 Angular,让你决定是要转投 Vue 3, React 19, Svelte 5 还是继续留在 Angular 阵营。

 

The Concept Behind the Change

Angular 长久以来一直有一个诟病 -- 学习门槛太高。

这绝对是千真万确的事情。如果有人告诉你学习 Angular 很简单,上手很快,那你要先问清楚,是他教会了很多人快速掌握 Angular,还是只是他自己快速掌握了 Angular。

这是两个完全不同的概念,他自己掌握或许只是因为他比一般人悟性高而已,千万不能以偏概全,不然会误人子弟的。

为什么 Angular 学习门槛会这么高呢?太多太多原因了,一句话总结就是 "不爱创新,爱 follow 标准,爱小题大" 再加一个 "不在乎用户"。

所以,新团队的第一个方向就是降低 Angular 的学习门槛。那要怎样降低呢?

简单丫,把一堆概念去掉,不就变得简单了吗。

  1. 去除 NgModule

    所谓的去除其实是 optional 的意思,好好的功能怎么可能删掉嘛,只是不逼着你学,不逼你用而已。

    NgModule 适合用来批量管理组件,但如果组件少的话,就会变成 1 个 NgModule 只管理 1 个组件,这就很多此一举啊,一个有什么好管理的?

  2. 去除 Decorator

    Decorator 简直是乱七八杂的东西,草案了这么久,后来又大改。虽然现在是定案了,但生态也没起来 (esbuild 就不支持 Decorator)

  3. 去除 Zone.js

    Zone.js 本来是不错的,但很遗憾,最终没能进入 ECMA。那 monkey patching 的东西谁还敢用呢?

  4. 去除 RxJS

    RxJS 是很好用,但是要学啊。必须改成 optional。

  5. 去除 Structural Directive

    结构型指令的语法叫微语法 (Syntax Reference)。

    微语法是挺灵活的,也支持扩展,但学习成本也不少。

    而但绝大部分时候,我们只有在使用原生结构型指令 *ngIf, *ngFor 时才用到微语法。

    这就很没必要学啊。

好,以上几个就是 Angular v14 以后改变的方向。未来还会不会出现 ”去除 TypeScript“ 或 "去除 OOP",那我就不晓得了🤪。

 

Optional NgModule の Standalone Component

Angular v14 以前,组件一定要依附在 NgModule 上,然后 NgModule import 另一个 NgModule 让组件可以相互使用,一个 NgModule 管理一批组件。

站管理角度,分组批量管理组件是正确的。但对于小项目而言,很多时候 1 个 NgModule 里面就只 declare 了一个组件,因为就没有那么多组件丫。

这种情况 NgModule 就显得很多余,为了写而写,为了管理而管理,这是不对的。

Angular v14 以后,组件可以单纯存在,不需要再依附 NgModule。组件也可以直接 import 另一个组件达到相互使用的结果。NgModule 变成 optional 了。

@Component({
  selector: 'app-test',
  standalone: true, // 在 @Component 声明 standalone: true 就可以了
  templateUrl: './test.component.html',
  styleUrl: './test.component.scss'
})
export class TestComponent {}

直接 import 就能用了。

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TestComponent], // 直接 import 组件, no more NgModule
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent {}

使用

<app-test />

注:v16 支持了 self-closing-tag 写法

效果

Angular 17+ 高级教程 – 盘点 Angular v14 到 v17 的重大改变

App 组件变成 Standalone Component 后,bootstrap 的方法就不同了

bootstrapApplication(
  AppComponent, 
  { providers: [] }
)
.catch((err) => console.error(err));

Provider 不写在 NgModule.providers 而是写在 bootstrapApplication 函数的参数。

想深入理解 NgModule 请看这篇 Angular 17+ 高级教程 – NgModule。

 

Optional Decorator の inject, input, output, viewChildren, contentChildren

提醒:Angular 要 optional 很多概念,这个过程是循序渐进的,这里要说的 Optional Decorator 不是说整个项目完完全全不写 Decorator,目前只是部分地方可以 optional 而已。

inject 函数

下面是 Dependency Injection 依赖注入 Decorator 的写法

export class TestComponent {
  constructor( 
    @SkipSelf() @Optional() @Inject(CONFIG_TOKEN) config: Config,
    @Attribute('value') value: string
  ) {
    console.log(config);
    console.log(value);
  }
}

下面是 v14 后,用 inject 函数替代 Decorator 的写法。

export class TestComponent {
  constructor() {
    const config = inject(CONFIG_TOKEN, { optional: true, skipSelf: true });
    const value = inject(new HostAttributeToken('value'));
  }
}

想深入理解 Dependancy Injection 请看这两篇  Dependency Injection 和 NodeInjector。

input, output 函数

下面是组件 input, output Decorator 的写法

export class TestComponent {
  @Input({ required: true, transform: numberAttribute })
  age!: number;

  @Output('timeout')
  timeoutEventEmitter = new EventEmitter();
}

注:input required 是 v16 的功能

下面是 v14 后,用 input 和 output 函数替代 Decorator 的写法。

export class TestComponent {
  age = input.required({ transform: numberAttribute });

  timeoutEventEmitter = output({ alias: 'timeout' })
}

v14 的写法显然没有以前整齐了 (无法一眼分辨哪些 property 是 input, output),

但没办法,为了去除 Decorator...只能牺牲整齐度了。

另外一个重点,input 函数不仅仅替代了 Decorator,它还引入了 Signal 概念。

input 函数的返回类型是 Signal 对象。

想深入理解 Signal 请看这篇 Angular 17+ 高级教程 – Signals。

viewChildren, contentChildren 函数

下面是组件 query element Decorator 的写法

export class TestComponent {
  @ViewChildren('item', { read: ElementRef })
  itemElementRefQueryList!: QueryList<ElementRef<HTMLElement>>;

  @ViewChild('item', { read: ElementRef })
  itemElementRef!: ElementRef<HTMLElement>;

  @ContentChildren('product', { read: ElementRef })
  productRefQueryList!: QueryList<ElementRef<HTMLElement>>;

  @ViewChild('product', { read: ElementRef })
  productElementRef!: ElementRef<HTMLElement>;
}

下面是 v14 后,用 viewChildren 和 contentChildren 函数替代 Decorator 的写法。

export class TestComponent {
  itemElementRefs = viewChildren('item', { read: ElementRef });
  itemElementRef = viewChild.required('item', { read: ElementRef });
  productElementRefs = contentChildren('product', { read: ElementRef });
  productElementRef = contentChild.required('product', { read: ElementRef });
}

它们返回的类似也是 Signal 哦。

想深入理解 Query Elements 请看这篇 Component 组件 の Query Elements。

 

Optional Zone.js

Zone.js 是用来 detect ViewModel change 的,没有了它要怎样 detect change 呢?

答案是 Signal。

在 main.ts 用 ɵprovideZonelessChangeDetection 函数把 Zone.js 关掉

import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { ɵprovideZonelessChangeDetection } from '@angular/core';

bootstrapApplication(
  AppComponent, 
  { providers: [ɵprovideZonelessChangeDetection()] }
)
.catch((err) => console.error(err));

使用 signal 对象作为属性值

export class AppComponent {
  value = signal(0);
 
  startTimer() {
    setInterval(() => {
      this.value.update(v => v + 1);
    }, 500);
  }
}

App Template

<p>{{ value() }}</p>
<button (click)="startTimer()" >start timer</button>

效果

Angular 17+ 高级教程 – 盘点 Angular v14 到 v17 的重大改变

模板会自动 tracking Signal 对象值的变化,每当值改变就会 refresh LView。

在 v14 以前,我们要实现相同的效果,需要手动 ChangeDetectorRef.markForCheck。

export class AppComponent {
  constructor(private changeDetectorRef: ChangeDetectorRef) {}
  
  value = 0;
 
  startTimer() {
    setInterval(() => {
      this.value++;
      this.changeDetectorRef.markForCheck();
    }, 500);
  }
}

或者使用 RxJS + AsyncPipe

export class AppComponent {
  constructor() {}

  value = new BehaviorSubject(0);
 
  startTimer() {
    setInterval(() => {
      this.value.next(this.value.value + 1);
    }, 500);
  }
}
<p>{{ value | async }}</p>
<button (click)="startTimer()" >start timer</button>

你更喜欢哪一种写法呢?我猜应该是...Svelte 5 吧🤪? 

想深入理解 Change Detection 请看这篇 Angular 17+ 高级教程 – Change Detection。

 

Optional RxJS

Signal 很像 RxJS 的 BehaviorSubject,而 BehaviorSubject 也是一种 Observable,所以在一些情况下,Signal 确实可以替代 RxJS,使得 RxJS 成为 optional。

下面是 RxJS 的写法

const firstNameBS = new BehaviorSubject('Derrick');
const lastNameBS = new BehaviorSubject('lastName');
const fullName$ = combineLatest([firstNameBS, lastNameBS]).pipe(
  map(([firstName, lastName]) => `${firstName} ${lastName}`)
);
fullName$.subscribe(fullName
=> console.log('fullName', fullName)); setTimeout(() => { firstNameBS.next('Alex'); lastNameBS.next('Lee'); }, 1000);

下面是 Signal 的写法

export class AppComponent {
  constructor() {
    const firstName = signal('Derrick');
    const lastName = signal('Yam');
    const fullName = computed(() => firstName() + ' ' + lastName());

    effect(() => console.log('fullName', fullName()))

    setTimeout(() => {
      firstName.set('Alex');
      lastName.set('Lee');
    }, 1000);
  }
}

是不是挺像的?写法上差不多,但实际运行的逻辑还是有一些不同哦。

下面是一个把 RxJS Observable 转换成 Signal 的例子

import { toSignal } from '@angular/core/rxjs-interop';
constructor() {
  const number$ = new Observable(subscriber => {
    let index = 0;
    const intervalId = window.setInterval(() => subscriber.next(index++), 1000);
    return () => {
      window.clearInterval(intervalId);
    }
  });

  const number = toSignal(number$);
  
  effect(() => console.log(number())); // 会一直 log
}

toSignal 会 subscribe number$ 然后一直接收新值,effect 可以监听每一次值得变化。

Optional RxJS 目前还只停留在 planning 中。Angular built-in 的 Router, HttpClient, ReactiveForms 依然是返回 RxJS Observable。

想深入理解 Signal 请看这篇 Angular 17+ 高级教程 – Signals。

 

Optional Structural Directive Syntax Reference (结构型指令微语法)

下面是一个常见的结构型指令微语法

 <h1 *ngIf="user$ | async as user; else loading">{{ user.firstName }}</h1>
 <ng-template #loading>loading...</ng-template>

这还是优化过的版本哦,没有优化更丑,请看

<ng-template [ngIf]="user$ | async" let-user="ngIf" [ngIfElse]="loading">
  <h1>{{ user.firstName }}</h1>
</ng-template>
<ng-template #loading>loading...</ng-template>

里面涉及了很多知识:AsyncPipe, as syntax, else syntax, Template Variable, ng-template, ng-template as ng-container 等等。

下面是 v14 后,用 Control Flow 替代结构型指令的写法。

@if (user$ | async; as user) {
  <h1>{{ user.firstName }}</h1>
} 
@else {
  loading...
}

是什么干净了很多?

换上 Signal 版本

@if (user(); as user) {
  <h1>{{ user.firstName }}</h1>
} 
@else {
  loading...
}
export class AppComponent {
  user = toSignal(new BehaviorSubject({ firstName: 'Derrick' }).pipe(delay(2000)));
}

效果

Angular 17+ 高级教程 – 盘点 Angular v14 到 v17 的重大改变

Control Flow 可以替代 *ngIf, *ngFor, *ngSwitch 指令。

想深入理解 "结构型指令微语法" 请看这篇 Structural Directive (结构型指令) & Syntax Reference (微语法)

想深入理解 Control Flow 请看这篇 Component 组件 の Control Flow。

 

其它小改动

以上这些都是 Angular v14 - v17 为降低学习门槛所做出的改动。

当然 v14 - v17 远远不只改动了这些,还加了许多新功能,这里我讲几个比较常用到的,有兴趣的可以点击链接查看:

  1. Typed Forms (v14)

  2. setInput (v14)

  3. Directive Composition API (v15)

  4. DestroyRef (v16)

  5. takeUntilDestroyed (v16)

  6. afterNextRender (v16)

  7. withComponentInputBinding (v16)

  8. input transform & required (v16)

 

我有必要升级改写法吗?

看到那么多改动,大家一定心里很焦虑,有种 AngularJS 被抛弃的感觉。

但其实呢...大家根本不用瞎焦虑。

这些改动都只是表层而已,底层 Ivy rendering engine 压根就没动过。

要知道,Angular 现在是在做减法,而不是加法。

我们跟着升级是很安全的,breaking changes 不多。

好,升级可以,那写法要改吗?

告诉你一个秘密,Angular Material 源码里:

  1. 一堆的 @Inject, @Input

  2. 一堆的 NgZone

  3. 一行 Signal 也没有

所以大家根本不用急,版本升级是必要的,有写新代码就用新的写法就可以了。

确实会有一些功能 (比如 Directive Composition API) 只有新写法支持,但是这种情况很少的,

要记得,他们底层 Ivy rendering engine 压根就没动过 (不然你以为 Angular 团队真的突飞猛进??)

所以,我非常鼓励大家升级 Angular 版本,至于写法嘛...不急

我自己体会了几个月,有一些新写法会比之前好,但有一些只是半径八两,我建议大家可以先学起来,有机会就写一写体会一下,总之不着急。

 

Next Big Thing の Wiz

v18, v19 估计 Signal 就要完结了。

Angular 下一个 Big Thing 是结合 Wiz,相关资讯:Angular and Wiz Are Better Together

科普一下,Wiz 是 Google 内部没有开源的框架。

Google 有很多 Web Application (比如:Google Ads, Analytics, Search Console, Tag Manager, Cloud 等等)

另外还有一些 Website (比如:Youtube, Search, Google Photos 等等)

它们可以分成两大派系,一个注重速度,一个注重交互。

目前重交互的通常使用 Angular,重速度的则使用 Wiz。

Wiz 的作者是大名鼎鼎的 Malte Ubl,现任 Vercel CTO,没错,就是那个 React 的 Next.js。

所以你大概可以遇见 Angular 之后会往 Next.js 的方向走,加强 SSR 和 SSG。

v17 推出的 Control Flow 除了有 @if, @for, @switch 之外还有一个叫 @defer,这个是全新的概念,它的灵感就是来源之 Wiz。

所以大家不需要太焦虑,跟着 Angular 慢慢走就可以了,记住,现在这群人的特色:"爱 follow 市场,爱新用户,爱借其它团队的力" 再加上 "有 planning"。

 

 

目录

上一篇 Angular 17+ 高级教程 – 学以致用

下一篇 TODO

想查看目录,请移步 Angular 17+ 高级教程 – 目录文章来源地址https://www.toymoban.com/news/detail-855082.html

到了这里,关于Angular 17+ 高级教程 – 盘点 Angular v14 到 v17 的重大改变的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Angular 17+ 高级教程 – Change Detection

    虽然 Angular 正在把大部分 Change Detection 概念换成 Signal,但是最快也要 1 年后,所以还是有必要认真学习一下的。   MVVM 框架的开发方式是这样的: 写 HTML 写 ViewModel 在 HTML 里加入 binding syntax。 在 HTML 里加入 listening syntax,在事件发生时修改 ViewModel。 MVVM 的宗旨是 \\\"不要直接

    2024年03月09日
    浏览(30)
  • Angular 17+ 高级教程 – 学以致用

    读这么多原理,到底为了什么?真实项目中真的会用得到吗? 你正在疑惑 \\\"知识的力量\\\" 吗? 本篇会给一个非常非常好的案例,让你感悟 -- 知识如何用于实战。 记住,我的目的是让你感悟,而不是要你盲目相信知识。   下面是我在 2020-11-06 记入的一个问题。 一模一样的问题

    2024年04月22日
    浏览(25)
  • Angular 17+ 高级教程 – Angular 的局限 の Query Elements

    熟悉 Angular 的朋友都知道,Angular 有非常多的局限,许多事情它都做不好,打开 Github 一堆 2016 - 2017 的 Issues,时至今日都没有解决。 原因也很简单 -- Angular 团队的不作为😔。 通常我会把常见的 Angular 的局限记入在这篇 Angular 的局限和 Github Issues,但由于本篇要讲的问题篇幅

    2024年04月24日
    浏览(28)
  • Angular 17+ 高级教程 – Dependency Injection 依赖注入

    本来是想先介绍 Angular Component 的,但 Component 里面会涉及到一些 Dependency Injection (简称 DI) 的概念,所以还是先介绍 DI 吧。 温馨提醒:如果你对 JS class、prototype 不太熟悉的话,建议你先看这篇 JavaScript – 理解 Object, Class, This, Prototype, Function, Mixins   首先我们有一个 class Ser

    2024年03月09日
    浏览(49)
  • Angular 17+ 高级教程 – Component 组件 の Control Flow

      Control Flow 是 Angular v17 版本后推出的新模板语法,用来取代 NgIf、NgForOf、NgSwitch 这 3 个 Structure Directive。 Structure Directive 的好处是比较灵活,原理简单,但是即便用了微语法,它看上去还是相当繁琐,而且不够优雅。 Conrol Flow 的好处是它的语法够美,缺点是不必 Structure Di

    2024年03月11日
    浏览(39)
  • Angular 17+ 高级教程 – Component 组件 の Query Elements

    Angular 是 MVVM 框架。 MVVM 的宗旨是 \\\"不要直接操作 DOM\\\"。 在 Component 组件 の Template Binding Syntax 文章中,我们列举了一些常见的 DOM Manipulation。 Template Binding Syntax 替代了上面许多的 DOM Manipulation,但任然有些 DOM Manipulation 是它没有覆盖到的。 比如说 Query Child Elements e.g. document.

    2024年03月09日
    浏览(31)
  • Angular 17+ 高级教程 – Component 组件 の Dependency Injection & NodeInjector

    在 Dependency Injection 依赖注入 文章中,我们学习了 50% 的 Angular DI 知识,由于当时还不具备组件知识,所以我们无法完成另外 50% 的学习。 经过了几篇组件教程后,现在我们已经具备了基础的组件知识,那这一篇我们便来完成 Angular DI 所有内容吧。   Angular in Depth – A Deep

    2024年03月09日
    浏览(30)
  • Angular 17+ 高级教程 – Component 组件 の ng-template

    上一篇 Dynamic Component 我们有提到,作为 MVVM 框架的 Angular 需要有方法替代掉 2 个 DOM Manipulation: document.createElement  template.content.clone Dynamic Component 便是替代 document.createElement 的方案。 而这篇我们要讲的 ng-template 则是 template.content.clone 的替代方案。   我们从浅入深,一个一

    2024年03月09日
    浏览(42)
  • Angular 17+ 高级教程 – Component 组件 の 生命周期钩子 (Lifecycle Hooks)

    之前在 Component 组件 の Angular Component vs Custom Elements 文章中,我们有学习过几个基础的 Lifecycle Hooks。 比如 OnChanges、OnInit、AfterViewInit、OnDestroy,但那篇只是微微带过而已。 这篇让我们来深入理解 Angular 的 Lifecycle Hooks。   在 Component 组件 の Dependency Injection NodeInjector 文章中

    2024年03月09日
    浏览(41)
  • Angular 17+ 高级教程 – Component 组件 の Structural Directive (结构型指令) & Syntax Reference (微语法)

    在 Attribute Directives 属性型指令 文章中,我们学习过了指令。指令是没有 HTML 和 CSS 的组件,它单纯用于封装 JS 的部分。 这一篇我们将继续学习另一种指令 -- Structural Directive 结构型指令。 就代码而言,Structural Directive 和 Attribute Directives 是完全一样的,只是用途不同,因此

    2024年03月10日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包