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

来自菜鸟教程
跳转至:导航、​搜索

介绍

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

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

先决条件

要完成本教程,您需要:

  • Node.js 安装在本地,您可以按照【X57X】如何安装Node.js 并创建本地开发环境【X126X】进行。

第 1 步 — 设置项目

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

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

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

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

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

cd vue-modal-component-example

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

npm run serve

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

有了这个脚手架,您就可以开始处理模态组件了。

第 2 步 - 创建模态组件

首先,在工程目录下,在src/components下新建一个Modal.vue文件。

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

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 来提供页眉、正文和页脚,但使用插槽将提供更大的灵活性。

使用 slots 允许您重用具有不同类型正文内容的相同模式。

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

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

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

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

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

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

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

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改变模板,增加showModalcloseModalisModalVisible

src/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 并替换默认内容:

src/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 包裹的组件或元素时,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 属性的 CSS 过渡将在 .5 秒内应用 ease 动画时间功能。

离开模态时,它会做相反的事情。 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 主题页面 以获取练习和编程项目。