Angular React Vue 比较 – 指令篇之自定义指令

Angular 与 Vue 除了内置的一系列指令之外,它们还允许你注册自定义的指令 。自定义指令主要是为了重用涉及普通元素的底层 DOM 访问的逻辑。

为了更好的做比较,我们还是按照 Angular 中创建属性型指令与结构型指令做为比较的基准。

创建属性型指令

我们将创建一个高亮指令,该指令将宿主元素的背景颜色设置为黄色。

Angular

创建

在 Angular中,@Directive() 装饰器的配置属性指定了该指令的 CSS 属性 Attribute 选择器 [appHighlight]

在指令的 constructor() 中添加 ElementRef,以便注入对宿主 DOM 元素的引用,即你应用 appHighlight 的元素。

import {Directive, ElementRef, inject} from '@angular/core';
@Directive({
  selector: '[appHighlight]',
})
export class HighlightDirective {
  private el = inject(ElementRef);
  constructor() {
    this.el.nativeElement.style.backgroundColor = 'yellow';
  }
}

应用

Angular 创建 HighlightDirective 类的实例,并将对 <p> 元素的引用注入到该指令的构造函数中,构造函数会将 <p> 元素的背景样式设置为黄色。

<p appHighlight>Highlight me!</p>

Vue

创建

一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数:

<script setup>
// 在模板中启用 v-highlight
const vHighlight = {
  mounted: (el) => {
    el.classList.add('is-highlight')
  }
}
</script>

应用

当 Vue 将元素插入到 DOM 中后,该指令会将一个 class 添加到元素中:

<template>
  <p v-highlight>This sentence is important!</p>
</template>

React

React 中并没有指令这一概念,可以使用自定义Hook + 组件来实现指定的效果。具体实现过程这里不再列举。

创建结构型指令

在 Angular 中结构型指令是应用于 <ng-template> 元素的指令,它们有条件地或重复地渲染 <ng-template> 的内容。

Vue 中很少用到自定义结构型指令,在 Vue 中结构型指令主要是使用 JS 来对 Dom 进行操作,系统本身并没有做过多的封装,React 中亦是如此。

这里主要通过 Angular 创建一个指令来了解结构型指令的概念。该指令从给定的数据源获取数据,并在数据可用时渲染其模板。这个指令被称为 SelectDirective,以 SQL 关键字 SELECT 命名,并使用属性选择器 [select] 与之匹配。

Angular

创建

Angular 创建指令类并指定 CSS 选择器 [select],该选择器在模板中标识该指令。导入 TemplateRef 和 ViewContainerRef。在指令中注入 TemplateRef 和 ViewContainerRef 作为私有属性。

import {Directive, TemplateRef, ViewContainerRef} from '@angular/core';
@Directive({
  selector: '[select]',
})
export class SelectDirective {
  private templateRef = inject(TemplateRef);
  private ViewContainerRef = inject(ViewContainerRef);
}

添加一个 selectFrom @Input() 属性。

export class SelectDirective {
  // ...
  @Input({required: true}) selectFrom!: DataSource;
}

现在 SelectDirective 已被搭建为具有输入的结构型指令,你现在可以添加逻辑来获取数据并使用它渲染模板:

export class SelectDirective {
  // ...
  async ngOnInit() {
    const data = await this.selectFrom.load();
    this.viewContainerRef.createEmbeddedView(this.templateRef, {
      // Create the embedded view with a context object that contains
      // the data via the key `$implicit`.
      $implicit: data,
    });
  }
}

应用

假设我们有以下数据源:

// 数据源定义
const source = {
  async load() {
    // 模拟API请求延迟
    await new Promise(resolve => setTimeout(resolve, 1000));
    return { 
      id: 123, 
      name: "Angular Guide", 
      author: "Dev Team" 
    };
  }
};

使用指令:

<p *select="let data from source" class="data-view">
  The data is: {{ data.name }} by {{ data.author }}
</p>

Vue

在 Vue 中很少用到自定义结构型指令,由于系统并没有对指令相关层面做过多的封装,如果要自定义结构型指令是基于 js 对 Dom 来操作。这里就不再列举示例。

React

React 中并没有指令这一概念,可以使用自定义Hook + 组件来实现指定的效果。具体实现过程这里不再列举。

小结

Angular 对于指令的封装更多一些,Vue 中的自定义指令常见于属性型指令,结构性的指令更多的是基于第三方库的调用而封装。React 则还是以自定义Hook + 组件来实现任何的指令操作。

本篇介绍的是如何创建自定义指令,是基于Angular的标准来进行的比较。

文章参考链接:

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注