Angular React Vue 比较 – 组件篇之Slots

如果我们需要向组件传递元素或组件,这个时候就要用到 Slots 了。在三大框架中,它们对这种传递方式的称呼不同,为了方便起见,我们把这种向组件传递元素或组件的方式统一称为 Slots 。

下表是三大框架中对于 Slot 的相关描述:

框架传递元素或组件的称呼描述
Angular内容投影内容投影是一种模式,你可以在其中插入或投影要在另一个组件中使用的内容。
Reactchildren prop可以将带有 children prop 的组件看作有一个“洞”,可以由其父组件使用任意 JSX 来“填充”。
Vue插槽不单单是传元素和组件用的,它本身还有作用域

Angular 组件的 Slots

在 Angular 组件中有单槽内容投影,多槽内容投影,条件内容投影三种,它的投影还在生命周期的钩子中有体现。

向组件传递单个 slot

单槽内容投影即是向组件传递单个 slot , Slot 在 Angular 组件模板中使用 <ng-content> 元素来显示。

组件 zippy-basic.component.ts

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

@Component({
  selector: 'app-zippy-basic',
  template: `
    <h2>Single-slot content projection</h2>
    <ng-content></ng-content>
  `
})
export class ZippyBasicComponent {}

模板 app.component.html

<app-zippy-basic>

  <!-- 向组件传递的单个 slot 内容 -->
  <p>Is content projection cool?</p>
</app-zippy-basic>

向组件传递多个 slot

多槽内容投影即是向组件传递多个 slot , 我们可以通过使用 <ng-content> 的 select 属性来完成此任务。

组件 zippy-multislot.component.ts

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

@Component({
  selector: 'app-zippy-multislot',
  template: `
    <h2>Multi-slot content projection</h2>

    Default:
    <ng-content></ng-content>

    Question:
    <ng-content select="[question]"></ng-content>
  `
})
export class ZippyMultislotComponent {}

使用 question 属性的内容将投影到带有 select=[question] 属性的 <ng-content> 元素。

模板 app.component.html

<app-zippy-multislot>
  <p question>
    Is content projection cool?
  </p>
  <p>Let's learn about content projection!</p>
</app-zippy-multislot>

我们还可以使用  ngProjectAs 属性来完成此操作。

<ng-container ngProjectAs="[question]">
  <p>Is content projection cool?</p>
</ng-container>

有了 ngProjectAs,就可以用 [question] 选择器将整个 <ng-container> 元素投影到组件中。

向组件有条件的传递 slot

如果我们的组件需要有条件地渲染内容或多次渲染内容,则应配置该组件以接受一个 <ng-template> 元素,其中包含要有条件渲染的内容。

在这种情况下,不建议使用 <ng-content> 元素,因为只要组件的使用者提供了内容,即使该组件从未定义 <ng-content> 元素或该 <ng-content> 元素位于 ngIf 语句的内部,该内容也总会被初始化。

下面的这个例子,即使在条件为假时,HelloWorld 组件也总是会被初始化。

组件 example-zippy.component.ts

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

@Component({
  selector: 'app-example-zippy',
  template: `
    <h2>根据 expanded 的状态加载 slot</h2>
    <button (click)="toggle()">Toggle</button>
    <ng-content *ngIf="expanded"></ng-content>
  `
})
export class ZippyBasicComponent {
  expanded = false;

  toggle() {
    this.expanded = ! this.expanded;
  }
}

模板 app.component.html

<app-example-zippy>
  <app-hello-world></app-hello-world>
</app-example-zippy>

请注意,在上面那个例子中 HelloWorld 组件总是会被初始化。这与我们的初衷不同,正常情况下应该是 expanded 的值为 true 时 HelloWorld 组件才会被初始化。

下面我们来调整一下,只有当 expanded 的值为真时 HelloWorld 组件才会被初始化。

调整后的组件 example-zippy.component.ts

import { Component, Directive, TemplateRef, ContentChild } from '@angular/core';

@Directive({
  selector: '[appZippyContent]'
})
export class ZippyExampleContentDirective {
  constructor(public templateRef: TemplateRef<unknown>) {}
}

@Component({
  selector: 'app-example-zippy',
  template: `
    <h2>Single-slot content projection</h2>
    <button (click)="toggle()">Toggle</button>
    <ng-container *ngIf="expanded" [ngTemplateOutlet]="content.templateRef"></ng-container>
  `
})
export class ExampleZippyComponent {
  expanded = false;
  @ContentChild(ZippyExampleContentDirective) content!: ZippyExampleContentDirective;

  toggle() {
    this.expanded = ! this.expanded;
  }
}

调整后的模板 app.component.html

<app-example-zippy>
  <ng-template appZippyContent>
    <app-hello-world></app-hello-world>
  </ng-template>
</app-example-zippy>

React 组件的 Slots

在 React 组件中,使用一个名称为 children 的 prop 来传递 JSX 。

向组件传递单个 slot

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

export default function Profile() {
  return (
    <Card>
      <p>card content</p>
    </Card>
  );
}

向组件传递多个 slot

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

export default function Profile() {
  return (
    <Card>
      <h2>Card Title</h2>
      <p>card content</p>
    </Card>
  );
}

向组件有条件的传递 slot

import { useState } from 'react';
import { HelloWorld } from './HelloWorld.js'

function Card({ children }) {
  const [showChildren, setShowChildren] = useState(false);

  return (
    <div className="card">
      { showChildren && children }
    </div>
  );
}

export default function Profile() {
  return (
    <Card>
      <HelloWorld />
    </Card>
  );
}

Vue 组件的 Slots

在 Vue 组件中,它提供了默认插槽和具名插槽,类似于 Angular 中的单槽内容投影和多槽内容投影。另外它还提供了作用域插槽,作用域插槽是 Vue 框架独有的功能,在本篇文章中不做介绍,感兴趣的同学可以移步 Vue 官网 作用域插槽 一节中查看。

向组件传递单个 slot

默认插槽即是向组件传递单个 slot , Slot 在 Vue 组件模板中使用 slot 元素来显示。

SubmitButton 组件代码片段

<button type="submit">
  <slot></slot>
</button>

插槽内容

<SubmitButton>Save</SubmitButton>

向组件传递多个 slot

通过具名插槽指定的名称可以向组件传递多个 slot 。

<slot> 元素可以有一个特殊的 attribute name,用来给各个插槽分配唯一的 ID 。没有提供 name 的 <slot> 出口会隐式地命名为“default” 。

BaseLayout 组件代码片段

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

要为具名插槽传入内容,我们需要使用一个含 v-slot 指令的 <template> 元素,并将目标插槽的名字传给该指令,v-slot 有对应的简写 # 。

向 <BaseLayout> 传递插槽内容的代码

<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <template #default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

向组件有条件的传递 slot

如果需要在组件中有条件的传递 slot , 可以在组件中使用 v-if 指令来控制。

<script setup>
import { ref } from 'vue'

const showHeader = ref(false)
const showBody = ref(true)
const showFooter = ref(false)
</script>

<template>
  <div class="container">
    <header v-if="showHeader">
      <slot name="header"></slot>
    </header>
    <main v-if="showBody">
      <slot></slot>
    </main>
    <footer v-if="showFooter">
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

小结

本章介绍了三大框架组件的 Slots ,对组件如何传递 slot 做了说明。

Angular 中向组件有条件的传递 slot 时应该使用 <ng-template> 元素,这样可以让组件根据我们想要的任何条件显式渲染内容,如果使用 <ng-content> ,无论条件是否成立,总会初始化插槽的内容。

React 组件中使用一个名称为 children 的 prop 来传递 JSX ,其实它并不存在 Angular 与 Vue中的单 slot 与 多 slot 分类。

Vue 组件中提供了作用域插槽,这是其他两个框架所不具备的功能,不过使用了作用域插槽后增加了组件之间的耦合性,笔者认为使用这个功能时还是需要慎重的。

文章参考链接:

  • 28
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AngularVueReact 都是流行的前端框架,它们都具有一些共同的特点,如组件化、虚拟 DOM、响应式数据流等。但它们也有一些不同之处,如下所述: 1. AngularAngular 是一个完整的框架,提供了一整套工具和库,包括模块化、组件化、依赖注入、模板语法、路由管理、HTTP 请求等功能。它使用 TypeScript 作为主要开发语言,强制使用严格类型检查。Angular 的学习曲线较陡峭,但它的文档和工具非常完善,可以帮助开发者快速构建可维护、可扩展、高性能的 Web 应用程序。 2. VueVue 是一个轻量级的框架,采用了类似 Angular组件化思想和类似 React 的虚拟 DOM 技术,但它的语法和模板更加简洁、易于理解。Vue 的学习曲线较为平缓,可以逐步学习,也可以通过 Vue-cli 等工具快速搭建项目。Vue 的社区和生态系统较为活跃,有很多插件和组件库可供使用。 3. ReactReact 是一个由 Facebook 开发的框架,采用了虚拟 DOM 和单向数据流的思想,可以帮助开发者构建高性能的 Web 应用程序。React 的语法较为灵活,可以使用 JSX 或纯 JavaScript 编写组件,但它需要使用额外的库和工具来实现路由、状态管理等功能。React 的生态系统非常丰富,有很多第三方组件和插件可供使用。 总之,AngularVueReact 都有自己的优点和适用场景,开发者可以根据项目需求和个人喜好选择合适的框架。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值