应用可能会需要在运行期间加载一些新的组件,这个时候我们就用到了动态组件。本章我们将通过一个动态广告条的示例来介绍三大框架是如何动态添加组件的。
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> 。
文章参考链接:
- https://angular.cn/guide/dynamic-component-loader
- https://angular.cn/api/core/ViewContainerRef
- https://angular.cn/api/core/ComponentRef
- https://zh-hans.react.dev/learn/synchronizing-with-effects
- https://cn.vuejs.org/guide/essentials/component-basics.html#dynamic-components
- https://cn.vuejs.org/api/built-in-special-elements.html