Nuxt3 手写一个全局弹窗 (Modal)

本文由 Loui 原创,如需转载,请先私信或评论。

背景

最近在做一个仿腾讯课堂的全栈开源项目,其中登录业务需要用到全局弹窗,因此自己做了一个,下面是最终效果和实现步骤,供大家参考、批评。

最终效果

我这里使用的是 Nuxt3 ,具体实现的代码和使用 Vue3 是差不多的。

此外,我还用到了以下两个内置组件:

名称 作用
<Teleport> 将组件的 DOM 结构转移到 <body>
<Transition> 给蒙层和弹窗体添加出入场动画

这张截图来自我正在做的仿腾讯课堂全栈开源项目,项目前端使用 Nuxt3,后端使用 NestJS。项目还未完成,如果你对这个项目感兴趣的话,可以关注我,完成后我会把开源的 Github 地址发出来。

实现步骤

第一步:封装弹窗组件

将蒙层和弹窗体分别作为两个 div 封装到一个组件中,使用响应式变量 isOpened 同时控制两个 div 的 v-show 。同时创建 open 和 close 两个控制弹窗显示隐藏方法,并使用 defineExpose 导出。

vue 复制代码
<script setup lang="ts">
const isOpened = ref(false)

// 关闭弹窗的方法
const close = function () {
  isOpened.value = false
}

// 开启弹窗的方法
// 这里准备了一个 opt 形参,用于开启弹窗时传入一些数据,你可以根据业务需要修改
const open = (opt?: any) => {
  isOpened.value = true
}

// 导出这两个方法
defineExpose({
  open,
  close,
})
</script>

<template>
  <teleport to="body">
    // 蒙层,已使用 Transition 包起来了,用于做出入场动画
    // 注意:这里设置了点击蒙层也会关闭弹窗
    <Transition name="mask">
      <div
        v-if="isOpened"
        class="mask"
        @click="close"
      />
    </Transition>
    // 弹窗体,已使用 Transition 包起来了,用于做出入场动画
    <Transition name="modal">
      <div v-if="isOpened" class="modal">
        本文由 Loui 原创,首发于掘金,如需转载,请先私信或评论。
      </div>
    </Transition>
  </teleport>
</template>

<style lang="scss" scoped>
.mask {
  position: fixed;
  inset-inline: 0 0;
  top: 0;
  bottom: 0;
  z-index: 9998;
  height: 100%;
  background-color: rgb(0 0 0 / 45%);
}

.modal {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 9999;
  width: 440px;
  min-height: 440px;
  padding: 20px 24px;
  background-color: #fff;
  border-radius: 12px;
  box-shadow:
    0 6px 16px 0 rgb(0 0 0 / 8%),
    0 3px 6px -4px rgb(0 0 0 / 12%),
    0 9px 28px 8px rgb(0 0 0 / 5%);
  transition: all 0.3s ease-in-out;
  transform: translate(-50%, -50%);
}

// 弹窗体的出入场动画:
.modal-enter-active,
.modal-leave-active {
  transition: all 0.3s ease;
}

.modal-enter-from,
.modal-leave-to {
  opacity: 0;
  transform: translate(-50%, -200%) scale(0);
}

// 蒙层的出入场动画:
.mask-enter-active,
.mask-leave-active {
  transition: opacity 0.3s ease;
}

.mask-enter-from,
.mask-leave-to {
  opacity: 0;
}
</style>

第二步:在顶层页面引入该组件,并使用 provide 注入它

在顶层页面中,比如说 App.vue 、layouts/default.vue 里,引入该弹窗组件,并使用 provide 方法,将该组件的模板引用提供给所有的子页面、子组件。

我这里是在 layouts/default.vue 里引入了该弹窗组件:

vue 复制代码
<script lang="ts" setup>
const loginModal = ref<any>(null) // 获取弹窗组件的模板引用
provide('loginModal', loginModal) // 提供给所有的子页面、子组件
</script>
<template>
  <MyHeader>
  <slot />
  <MyFooter />
  <LoginModal ref="loginModal" />
</template>

第三步:在需要使用弹窗的页面,使用 inject 接收它

vue 复制代码
<script lang="ts" setup>
// 注意:如果要支持 ts,这里一定要记得将组件的类型导入进来,Nuxt 不会自动引入
import type LoginModal from '@/components/LoginModal.vue'
// 使用 inject 方法,接收弹窗的模板引用,同时使用类型断言 as 设置它的类型
const loginModal = inject('loginModal') as InstanceType<typeof LoginModal>
</script>
<template>
    <div class="login" @click="loginModal.open()">
      登录
    </div>
</template>

总结

这套代码其实还有很多可以完善的地方,如果你有其他更好的实现方法或者思路,欢迎在评论区补充。如果你发现了什么 Bug 或者有其它问题,也欢迎在评论区提出来。

如果这篇文章有帮到你,还请点一个不要钱的赞,感谢支持 ❤️

相关推荐
用户061760544431 天前
关于Nuxt3+Vue3的基础使用
nuxt.js
程序员爱钓鱼7 天前
在 Nuxt 3 中实现和使用 SEO 数据:通过 useState 管理全局 SEO 信息
vue.js·后端·nuxt.js
仿生狮子11 天前
Reka UI 是个啥?
vue.js·nuxt.js·ui kit
MurphyChen12 天前
🧭 React 组件通信指南:父传子、子传父、任意组件通信
前端·react.js·nuxt.js
四木呀16 天前
Nuxt3 实现接口域名动态化
前端·nuxt.js
朝阳391 个月前
Nuxt.js 3【详解】服务器 Server
服务器·javascript·nuxt.js
wxz9992 个月前
nuxt2项目
javascript·nuxt.js
vortesnail3 个月前
Nuxt3 切换主题色且不闪屏该怎么做?
前端·vue.js·nuxt.js
陶然同学3 个月前
【学生管理系统】权限管理之角色管理
java·elementui·nuxt.js·学生管理系统
陶然同学3 个月前
【学生管理系统】权限管理之用户管理
java·nuxt.js·学生管理系统