Angular React Vue 比较 – 组件篇之动态组件

应用可能会需要在运行期间加载一些新的组件,这个时候我们就用到了动态组件。本章我们将通过一个动态广告条的示例来介绍三大框架是如何动态添加组件的。

Angular中的动态组件

下面的例子展示了如何构建动态广告条。

指令

在添加组件之前,先要定义一个锚点来告诉 Angular 要把组件插入到什么地方。广告条使用一个名叫 AdDirective 的辅助指令来在模板中标记出有效的插入点。AdDirective 注入了 ViewContainerRef 来获取对容器视图的访问权,这个容器就是那些动态加入的组件的宿主。

ad.directive.ts 文件

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[adHost]',
})
export class AdDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

加载组件

在下的代码中 <ng-template> 元素就是刚才制作的指令将应用到的地方。通过使用 adHost 指令,Angular 就知道该把组件动态加载到哪里了。

要把广告组件添加到模板中,我们可以调用 ViewContainerRef 的 createComponent()。

ad.banner.component.ts 文件

import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { AdDirective } from './ad.directive';
import { AComponent, BComponent, CComponent } from './ads.component';

@Component({
  selector: 'app-ad-banner',
  template: `
    <div class="ad-banner-example">
      <h3>动态广告条</h3>
      <ng-template adHost></ng-template>
    </div>
  `
})
export class AdBanner3Component implements OnInit, OnDestroy {
  ads = [AComponent, BComponent, CComponent];
  currentAdIndex = -1;
  @ViewChild(AdDirective, {static: true}) adHost!: AdDirective;

  private clearTimer: VoidFunction | undefined;

  ngOnInit(): void {
    this.loadComponent();
    this.getAds();
  }

  ngOnDestroy() {
    this.clearTimer?.();
  }

  loadComponent() {
    this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;
    const adItem = this.ads[this.currentAdIndex];

    const viewContainerRef = this.adHost.viewContainerRef;
    viewContainerRef.clear();
    viewContainerRef.createComponent(adItem);
  }

  getAds() {
    const interval = setInterval(() => {
      this.loadComponent();
    }, 3000);
    this.clearTimer = () => clearInterval(interval);
  }
}

广告条

在广告条中,我们列出了三个广告组件,这三个组件就是上面代码中动态加载的组件。

ads.component.ts 文件

import { Component } from '@angular/core';

@Component({
  template: `
    <div class="ad-a">
      <p>Ad A</p>
    </div>
  `
})
export class AComponent {

}

@Component({
  template: `
    <div class="ad-b">
      <p>Ad B</p>
    </div>
  `
})
export class BComponent {

}

@Component({
  template: `
    <div class="ad-c">
      <p>Ad C</p>
    </div>
  `
})
export class CComponent {

}

React中的动态组件

在 React 中,并没有提出动态组件的概念,不过由于 React 的模板使用的是 JSX 语法,我们通过对状态设置成不同的组件值来实现动态添加组件的功能。

下面的例子展示了如何构建动态广告条。

import React from "react";
import { useState, useEffect } from 'react';

export default function AdBannerComponent() {
    const [ component, setComponent ] = useState( '' );
    const ads = [ <AdComponentA />, <AdComponentB />, <AdComponentC /> ];
    let currentAdIndex = -1;

    useEffect(() => {
      const interval = setInterval(() => {
        loadComponent();
      }, 3000);

      return () => clearInterval(interval);
    }, []);

    function loadComponent() {
      currentAdIndex = (currentAdIndex + 1) % ads.length;
      setComponent( ads[currentAdIndex] );
    }

    return (
      <div>
        <h2>动态广告条</h2>
        { component }
      </div>
    )
}

function AdComponentA() {
  return (
    <p>Ad A</p>
  )
}

function AdComponentB() {
  return (
    <p>Ad B</p>
  )
}

function AdComponentC() {
  return (
    <p>Ad C</p>
  )
}

Vue中的动态组件

在 Vue 中,提供了 <component> 元素和特殊的 is attribute 来实现动态组件。

下面的例子展示了如何构建动态广告条。

AdBanner.vue 文件

<script setup>
import { shallowRef, onMounted, onUnmounted } from 'vue';

import AdComponentA from './components/AdComponentA.vue';
import AdComponentB from './components/AdComponentB.vue';
import AdComponentC from './components/AdComponentC.vue';


const adItem = shallowRef(AdComponentA);
const ads = [ AdComponentA, AdComponentB, AdComponentC]; 
let currentAdIndex = -1;
let interval;

onMounted(() => {
  interval = setInterval(() => {
    loadComponent();
  }, 3000);
});

onUnmounted(() => {
  clearInterval(interval);
});

function loadComponent() {
  currentAdIndex = (currentAdIndex + 1) % ads.length;
  adItem.value = ads[currentAdIndex];
}

</script>

<template>

  <h2>动态广告条</h2>
  <component :is="adItem" />
</template>

AdComponentA.vue 文件

<script setup>
</script>

<template>
  <p>Ad A</p>
</template>

AdComponentB.vue 文件

<script setup>
</script>

<template>
  <p>Ad B</p>
</template>

AdComponentC.vue 文件

<script setup>
</script>

<template>
  <p>Ad C</p>
</template>

小结

本章介绍了三大框架的动态组件,对如何加载动态组件做了说明。

Angular 使用 ViewContainerRef 类中的 createComponent() 方法来创建组件。通过 createComponent() 方法返回的引用,我们还可以设置它的属性或调用它的方法。

React 的模板使用的是 JSX 语法,通过对状态设置成不同的组件值就可以实现动态组件的功能。

Vue 提供了一个用于渲染动态组件或元素的“元组件” <component> 。

文章参考链接:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容