如何使用 Vue.js 构建模态组件

介绍

模态是一种用户体验约定,用于将用户的注意力引导到他们需要阅读或与之交互的一段内容上。这些往往以小块内容的形式直接出现在用户的视野中,并带有某种背景,或者模糊或隐藏页面上的其余内容。如果用户希望返回该页面,他们必须参与该模式或可能将其关闭。

在本文中,您将学习如何使用 Vue.js 中的转换和插槽创建可重用的模态组件。

先决条件

要完成本教程,您需要:

步骤 1 — 设置项目

您可以使用@vue/cli来创建一个新的 Vue.js 项目。

在终端窗口中,使用以下命令:

  • npx @vue/cli create --default vue-modal-component-example

这将使用默认配置来创建 Vue.js 项目。

导航到新创建的项目目录:

  • cd vue-modal-component-example

启动项目以验证没有错误。

  • npm run serve

如果您localhost:8080在 Web 浏览器中访问本地应用程序(通常位于),您将看到一条"Welcome to Your Vue.js App"消息。

设置好这个脚手架后,您可以开始处理模态组件。

第 2 步 – 创建模态组件

首先,在项目目录中,Modal.vuesrc/components.

让我们从定义模板开始。您将需要 adiv作为背景阴影,adiv作为模态框,以及一些元素来定义其结构:

src/components/Modal.vue
<template>
  <div class="modal-backdrop">
    <div class="modal">
      <slot name="header">
      </slot>

      <slot name="body">
      </slot>

      <slot name="footer">
      </slot>
    </div>
  </div>
</template>

您可以使用 props 来提供页眉、正文和页脚,但使用插槽将提供更大的灵活性。

插槽的使用允许您对不同类型的正文内容重用相同的模态。

您可以使用模态来显示消息,但您可能希望对表单重复使用相同的模态来提交请求。

虽然 props 通常足以构建一个组件,但是通过 props 提供 HTML 需要我们使用v-html它来渲染它——这可能导致 XSS 攻击

在这里,您使用命名插槽来允许在同一组件中使用多个插槽。

当您定义一个命名插槽时,您使用该名称标识的任何内容都将被渲染而不是原始插槽。把它想象成一个占位符。与占位符一样,插槽也可以有一个默认内容,当没有提供时将呈现该内容。

因为提供的内容替换了<slot>标签,为了保证节有你想要的类,你需要包装每个插槽。

让我们为插槽、包装元素和初始 CSS 设置一些默认值,使其看起来像一个基本的模态。

在模板上方,添加组件名称和关闭模态的方法:

src/components/Modal.vue
<script>
  export default {
    name: 'Modal',
    methods: {
      close() {
        this.$emit('close');
      },
    },
  };
</script>

然后,修改模板以包装插槽,提供默认值,并显示用于关闭模态的按钮:

src/components/Modal.vue
<template>
  <div class="modal-backdrop">
    <div class="modal">
      <header class="modal-header">
        <slot name="header">
          This is the default title!
        </slot>
        <button
          type="button"
          class="btn-close"
          @click="close"
        >
          x
        </button>
      </header>

      <section class="modal-body">
        <slot name="body">
          This is the default body!
        </slot>
       </section>

      <footer class="modal-footer">
        <slot name="footer">
          This is the default footer!
        </slot>
        <button
          type="button"
          class="btn-green"
          @click="close"
        >
          Close Modal
        </button>
      </footer>
    </div>
  </div>
</template>

接下来,在模板下方,为组件添加样式:

src/components/Modal.vue
<style>
  .modal-backdrop {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: rgba(0, 0, 0, 0.3);
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .modal {
    background: #FFFFFF;
    box-shadow: 2px 2px 20px 1px;
    overflow-x: auto;
    display: flex;
    flex-direction: column;
  }

  .modal-header,
  .modal-footer {
    padding: 15px;
    display: flex;
  }

  .modal-header {
    position: relative;
    border-bottom: 1px solid #eeeeee;
    color: #4AAE9B;
    justify-content: space-between;
  }

  .modal-footer {
    border-top: 1px solid #eeeeee;
    flex-direction: column;
    justify-content: flex-end;
  }

  .modal-body {
    position: relative;
    padding: 20px 10px;
  }

  .btn-close {
    position: absolute;
    top: 0;
    right: 0;
    border: none;
    font-size: 20px;
    padding: 10px;
    cursor: pointer;
    font-weight: bold;
    color: #4AAE9B;
    background: transparent;
  }

  .btn-green {
    color: white;
    background: #4AAE9B;
    border: 1px solid #4AAE9B;
    border-radius: 2px;
  }
</style>

所有这些部分放在一起完成了您的模态组件。您可以导入这个新组件App.vue并观察它的运行情况。

修改App.vue修改模板,并添加showModalcloseModal以及isModalVisible

源代码/App.vue
<template>
  <div id="app">
    <button
      type="button"
      class="btn"
      @click="showModal"
    >
      Open Modal!
    </button>

    <Modal
      v-show="isModalVisible"
      @close="closeModal"
    />
  </div>
</template>

<script>
  import modal from './components/Modal.vue';

  export default {
    name: 'App',
    components: {
      Modal,
    },
    data() {
      return {
        isModalVisible: false,
      };
    },
    methods: {
      showModal() {
        this.isModalVisible = true;
      },
      closeModal() {
        this.isModalVisible = false;
      }
    }
  };
</script>

此代码将导入Modal组件并显示一个Open Modal按钮以进行交互。

注意:在您的App.js文件中,您可以选择引用slots并替换默认内容:

源代码/App.js
<Modal
  v-show="isModalVisible"
  @close="closeModal"
>
  <template v-slot:header>
    This is a new modal header.
  </template>

  <template v-slot:body>
    This is a new modal body.
  </template>

  <template v-slot:footer>
    This is a new modal footer.
  </template>
</Modal>

在您的 Web 浏览器中查看应用程序,并通过打开和关闭它来验证该模式的行为是否符合预期。

第 3 步 – 添加过渡

您可以使用过渡使其看起来更平滑地打开和关闭。

Vue 提供了一个名为的包装组件transition,允许您添加进入和离开的过渡。这个包装器组件可用于任何元素或组件,它们允许 CSS 和 JavaScript 挂钩。

每次transition插入或移除由 a 包裹的组件或元素时,Vue 都会检查给定元素是否具有 CSS 转换,并会在正确的时间添加或移除它们。JavaScript 钩子也是如此,但在这种情况下,您将只使用 CSS。

添加或删除元素时,会为进入和离开过渡应用六个类它们中的每一个都将带有过渡名称的前缀。

首先,让我们开始向模态添加一个过渡包装组件:

src/components/Modal.vue
<template>
  <transition name="modal-fade">
    <div class="modal-backdrop">
      <div class="modal">
        ...
      </div>
    </div>
  </transition>
</template>

现在让我们使用应用的类为不透明度添加一个缓慢渐变的过渡:

src/components/Modal.vue
<style>
  ...

  .modal-fade-enter,
  .modal-fade-leave-to {
    opacity: 0;
  }

  .modal-fade-enter-active,
  .modal-fade-leave-active {
    transition: opacity .5s ease;
  }
</style>

最初,模态将显示为隐藏,opacity属性值为0

当模态打开时,它将.modal-fade-enter-active应用该类,并且opacity.5使用ease动画计时功能几秒钟内应用属性的 CSS 转换

当离开模态时,它会做相反的事情。modal-fade-leave-active课程将被应用,模式将淡出。

在 Web 浏览器中查看应用程序并验证模式是否淡入淡出。

第 4 步 – 添加辅助功能

您将希望使用ARIA 属性访问您的模态组件

添加role="dialog"将帮助辅助软件将组件识别为与用户界面的其余部分分开的应用程序对话框。

您还需要使用aria-labelledbyaria-describedby属性对其进行正确标记

aria-label为关闭按钮添加属性。

src/components/Modal.vue
<template>
  <transition name="modal-fade">
    <div class="modal-backdrop">
      <div class="modal"
        role="dialog"
        aria-labelledby="modalTitle"
        aria-describedby="modalDescription"
      >
        <header
          class="modal-header"
          id="modalTitle"
        >
          <slot name="header">
            This is the default title!
          </slot>
          <button
            type="button"
            class="btn-close"
            @click="close"
            aria-label="Close modal"
          >
            x
          </button>
        </header>

        <section
          class="modal-body"
          id="modalDescription"
        >
          <slot name="body">
            This is the default body!
          </slot>
        </section>

        <footer class="modal-footer">
          <slot name="footer">
            This is the default footer!
          </slot>
          <button
            type="button"
            class="btn-green"
            @click="close"
            aria-label="Close modal"
          >
            Close Modal
          </button>
        </footer>
      </div>
    </div>
  </transition>
</template>

模态组件的最终版本现在应该类似于:

src/components/Modal.vue
<script>
  export default {
    name: 'Modal',
    methods: {
      close() {
        this.$emit('close');
      },
    },
  };
</script>

<template>
  <transition name="modal-fade">
    <div class="modal-backdrop">
      <div class="modal"
        role="dialog"
        aria-labelledby="modalTitle"
        aria-describedby="modalDescription"
      >
        <header
          class="modal-header"
          id="modalTitle"
        >
          <slot name="header">
            This is the default tile!
          </slot>
          <button
            type="button"
            class="btn-close"
            @click="close"
            aria-label="Close modal"
          >
            x
          </button>
        </header>

        <section
          class="modal-body"
          id="modalDescription"
        >
          <slot name="body">
            This is the default body!
          </slot>
        </section>

        <footer class="modal-footer">
          <slot name="footer">
            This is the default footer!
          </slot>
          <button
            type="button"
            class="btn-green"
            @click="close"
            aria-label="Close modal"
          >
            Close me!
          </button>
        </footer>
      </div>
    </div>
  </transition>
</template>

<style>
  .modal-backdrop {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: rgba(0, 0, 0, 0.3);
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .modal {
    background: #FFFFFF;
    box-shadow: 2px 2px 20px 1px;
    overflow-x: auto;
    display: flex;
    flex-direction: column;
  }

  .modal-header,
  .modal-footer {
    padding: 15px;
    display: flex;
  }

  .modal-header {
    position: relative;
    border-bottom: 1px solid #eeeeee;
    color: #4AAE9B;
    justify-content: space-between;
  }

  .modal-footer {
    border-top: 1px solid #eeeeee;
    flex-direction: column;
  }

  .modal-body {
    position: relative;
    padding: 20px 10px;
  }

  .btn-close {
    position: absolute;
    top: 0;
    right: 0;
    border: none;
    font-size: 20px;
    padding: 10px;
    cursor: pointer;
    font-weight: bold;
    color: #4AAE9B;
    background: transparent;
  }

  .btn-green {
    color: white;
    background: #4AAE9B;
    border: 1px solid #4AAE9B;
    border-radius: 2px;
  }

  .modal-fade-enter,
  .modal-fade-leave-to {
    opacity: 0;
  }

  .modal-fade-enter-active,
  .modal-fade-leave-active {
    transition: opacity .5s ease;
  }
</style>

这是具有可访问性和转换的模态组件的基础。

结论

在本文中,您使用 Vue.js 构建了一个模态组件。

您尝试过slots让您的组件可重用,transitions以创建更好的用户体验,以及ARIA使您的组件更易于访问的属性。

如果您想了解有关 Vue.js 的更多信息,请查看我们的 Vue.js 主题页面以获取练习和编程项目。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁