Angular 17+ 高级教程 – Component 组件 の Angular Component vs Web Component

前言

在 初识 Angular 中我有提到, Angular 团队是一群不爱创新、爱 follow 标准、爱小题大做的一群人。

所以,要理解 Angular Component,我们就非得要先搞懂远古的 Web ComponentMVVM 的概念。

 

MVVM 和 Web Component

关于 MVVM 可以大约翻看这篇

简单说一下,MVVM 就是让我们尽量不要像 jQuery 年代那样直接操作 DOM,而是通过框架提供的接口,间接的去操作 DOM。

为什么要这样?

第一是,操作 DOM 的代码很繁琐不好理解。你要可读性高,本来就需要封装成声明式,而现在框架就替你封装好了。

第二是,Angular 是框架,它控制了很多底层的东西,你如果绕过它,直接控制底层,一不小心就会和它撞上。所以 best practice 总是告诉你,尽量不要直接操作 DOM。

注:不要误会哦。它是说不要直接操作,但我们可以间接操作(使用 Angular 开放好的接口)。还有 "尽量" 不代表完全不要,只是要可控就可以了。

关于 Web Component,请务必先读完 DOM – Web Components 这篇,因为下面我会用到里面的例子,继续展开。

 

用 Angular Component 重写 Counter Component

在 DOM – Web Components 文章的结尾,我写了一个 Counter Component,我们现在用 Angular 重写一遍。

最终效果是这样

Step by Step

follow Angular 复习与进阶系列 – Get Started 搭建一个测试环境(这里我就不使用 inline style 和 inline template了,我不喜欢。)

ng new my-app --style=scss --routing=false --skip-tests --ssr=false

创建 Counter Component

cd src/app
ng g c counter

进入 counter.component.ts, 它目前长这样

CounterComponent 是一个类,我们要用它来描述下面这个 UI 组件。(Thinking in Angular Way)

以面向对象的方式来看,中间的号码可以用一个属性(property)来表示。

左右加减可以改变中间的号码,它们可以用方法(method)来表示。

所以,添加 number 属性和 add、minus 方法到 CounterComponent 里。

export class CounterComponent {
  number = 0;

  minus() {
    this.number--;
  }

  plus() {
    this.number++;
  }
}
View Code

class 搞定,接下来我们进入 counter.component.html

先给它一个初始 HTML

<button>-</button>
<span>1</span>
<button>+</button>

我们要让它和刚才的 CounterComponent 对象,关联起来。

<span>{{ number }}</span>

这个是 Angular 其中一种关联语法。{{ number }} 就是对象里的 number 属性。

接着是左右按钮的点击事件,它们要关联到对象里的 add minus 方法。

<button (click)="minus()">-</button>
<span>{{ number }}</span>
<button (click)="plus()">+</button>

注:这个环节我们先不去探讨这些关联语法 binding syntax。我们继续往下。

接着就是加入 style

添加 class 到 HTML 作为 CSS selector

<button class="minus" (click)="minus()">-</button>
<span class="number">{{ number }}</span>
<button class="plus" (click)="plus()">+</button>

进入 counter.component.scss

:host {
  display: flex;
  gap: 16px;

  :is(.minus, .plus) {
    width: 64px;
    height: 64px;
  }

  .number {
    width: 128px;
    height: 64px;
    border: 1px solid gray;
    font-size: 36px;
    display: grid;
    place-items: center;
  }
}
View Code

没什么值得关注点,只是一些美观的 styles 而已。

至此,Counter Component 的部分就算完成了。

接着是如何使用它,我们进入到 app.component.ts。

import CounterComponent 到 AppComponent 中。

任何组件内想使用其它组件都必须先 import。

接着到 app.component.html

<app-counter></app-counter>

"app-" 是所有组件的 prefix,如果你不喜欢,可以通过 angular.json 做设置。

最后跑起来

ng serve --open

效果

 

对比 Angular Component 和 Web Component 实现的 Counter 组件

通过对比,我们可以看出 Angular 团队在设计 Angular Component 的思路,它们如何看待 Web Component 的缺陷,如何保留 Web Component 的设计理念,如何完善 Web Component。

最终保留了什么,丢弃了什么,增加了什么。搞清楚这些对学习和使用 Angular 非常重要,正所谓 Thinking in Angular Way 就是这样来的。

我们先理一下 Web Component 的整个流程

1. 定义 CounterComponent class 

2. 做 Shadow DOM 隔离 CSS

3. 拦截初始化,通过 ajax 获取 template(如果你可以接受直接写 Raw HTML 在 TS 则可以省略掉这一步...)

4. 搞事件监听和渲染(DOM 操作)

5. define and use

上面 5 个 steps,Angular 都实现了。只是大部分实现代码都被隐藏了起来,我们主要写的是声明代码。

短短的几行代码,Angular 就 "声明" 了以下 4 个 steps

1. 定义 CounterComponent class

@Component 表示这个 class 是 Component

2. 做 Shadow DOM 隔离 CSS

Angular by default 所有 Component 都是隔离 CSS 的,不过它并不是通过 Shadow DOM 实现的。之后的教程会详细讲到。

3. 拦截初始化,通过 ajax 获取 template

Angular 是在 compile time 去链接 .html file 的,而 @Component.templateUrl 声明了 file 的位置。

5. define and use

@component.selector 声明了匹配的 selector 

上面几个步骤, 我们都不需要写实现代码,只写声明代码就可以了。其它的交给 Angular。

还有一步是

4. 搞事件监听和渲染(DOM 操作)

这一步,Angular 用 MVVM 的方式来 "声明"。

同样的,我们不需要写任何操作 DOM 的代码。只要 "声明" 就可以了。.

从这些对比中,我们可以体会到,Angular 的设计理念就是尽可能把 "实现代码" 转换成 "声明代码"。

这个动机很好理解,一个长期维护的项目,代码分三种。 

1. 实现代码

2. 可读性代码

3. 管理代码

实现代码就是让逻辑跑起来的代码。它们的特色就是繁琐、啰嗦、可读性差、难修改。

可读性代码就是我们声明的变量、方法名、类、接口等等。它们只是为了 "好读",你随便取名字并不影响程序的执行。

管理代码就是我们为了方便维护、修改、扩展、而做的各种封装代码。

项目越小,越不重视可读性代码和管理代码。而 Angular 的定位是长期维护的大项目,所以它的方向一定是让开发人员尽可能减少实现代码,提升可读性和管理代码。

 

Angular Component !== Web Component

Angular Component 虽然很大程度上借鉴了 Web Component,但是 Angular 并不是用 Shadow DOM + Custom Element 来实现 Web Component 的哦。 

Angular 有一个扩展叫 Angular elements,它的方向是 convert Angular Component to 正真的 Web Component。也就是 Shadow Dom + Custom Element。

但目前这个项目有很多缺失的功能,而且没有得到足够的重视。希望未来不会被砍掉呗...

 

目录

上一篇 Angular 17+ 高级教程 – Dependency Injection 依赖注入

下一篇 Angular 17+ 高级教程 – Component 组件 の Angular Component vs Custom Elements

想查看目录,请移步 Angular 17+ 高级教程 – 目录

 

posted @ 2022-12-08 12:42  兴杰  阅读(367)  评论(0编辑  收藏  举报