Vue3 中如何在 setup 语法糖下,通过 Layer 弹窗组件弹出自定义 Vue 组件?

在 Vue3 的 setup 语法中使用 Layer 弹窗加载 Vue 组件,核心思路是 手动创建 Vue 组件实例并挂载到 Layer 生成的 DOM 容器中,同时处理组件的挂载/卸载生命周期和通信逻辑。以下是详细步骤和示例:

一、前置准备:引入 Layer

Layer 是 Layui 生态的弹窗插件,需先在 Vue3 项目中引入(支持 CDN 或 npm 安装):

方式 1:CDN 引入(简单快捷)

public/index.html 中添加 Layui + Layer 资源:

html 复制代码
<!-- 引入 Layui 样式(包含 Layer) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/layui-src/dist/css/layui.css">
<!-- 引入 Layui 核心(包含 Layer) -->
<script src="https://cdn.jsdelivr.net/npm/layui-src/dist/layui.js"></script>
方式 2:npm 安装(工程化项目)
bash 复制代码
npm install layui-src --save

在入口文件 main.js 中全局引入:

javascript 复制代码
import 'layui-src/dist/css/layui.css'
import layui from 'layui-src'
// 全局挂载 Layer 到 window(方便组件内使用)
window.layer = layui.layer

二、核心原理

  1. document.createElement 创建临时 DOM 容器;
  2. 用 Vue3 的 createApp + h 函数创建组件实例并挂载到临时容器;
  3. 调用 layer.open 显示弹窗,内容指定为临时容器;
  4. 弹窗关闭时,卸载 Vue 组件并移除临时 DOM(避免内存泄漏)。

三、完整示例

1. 编写待弹出的 Vue 组件(PopupComponent.vue)

支持 props 传参和事件回调(与父组件通信):

vue 复制代码
<!-- PopupComponent.vue -->
<template>
  <div class="popup-container">
    <h3 class="popup-title">{{ title }}</h3>
    <div class="popup-content">{{ content }}</div>
    <div class="popup-footer">
      <button @click="handleCancel">取消</button>
      <button @click="handleConfirm">确认</button>
    </div>
  </div>
</template>

<script setup>
// 接收父组件传递的参数
const props = defineProps({
  title: { type: String, default: '默认标题' },
  content: { type: String, default: '默认内容' }
})

// 向父组件触发事件
const emit = defineEmits(['confirm', 'cancel'])

// 确认按钮逻辑
const handleConfirm = () => {
  emit('confirm', '弹窗返回的数据:成功')
}

// 取消按钮逻辑
const handleCancel = () => {
  emit('cancel')
}
</script>

<style scoped>
.popup-container {
  padding: 20px;
}
.popup-title {
  margin: 0 0 15px;
  font-size: 18px;
}
.popup-content {
  margin: 15px 0;
  color: #666;
}
.popup-footer {
  text-align: right;
}
.popup-footer button {
  margin-left: 10px;
  padding: 6px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.popup-footer button:first-child {
  background: #eee;
}
.popup-footer button:last-child {
  background: #1E9FFF;
  color: white;
}
</style>
2. 在父组件中用 setup 语法调用弹窗

封装弹窗逻辑(可复用),处理组件挂载、通信和卸载:

vue 复制代码
<!-- ParentComponent.vue -->
<template>
  <button @click="openLayerPopup" class="open-btn">打开 Vue 组件弹窗</button>
</template>

<script setup>
import { createApp, h } from 'vue'
import PopupComponent from './PopupComponent.vue'

// 打开 Layer 弹窗(核心逻辑)
const openLayerPopup = () => {
  // 1. 创建临时 DOM 容器(用于挂载 Vue 组件)
  const tempContainer = document.createElement('div')
  document.body.appendChild(tempContainer)

  // 2. 创建 Vue 应用实例并挂载组件
  const app = createApp({
    // 用 h 函数渲染目标组件,传递 props 和事件
    render: () => h(PopupComponent, {
      title: 'Layer + Vue3 组件弹窗',
      content: '这是通过 Layer 弹出的 Vue 组件内容',
      // 监听组件的 confirm 事件
      onConfirm: (data) => {
        console.log('弹窗确认:', data)
        layer.msg(`成功:${data}`)
        layer.close(popupIndex) // 关闭弹窗
      },
      // 监听组件的 cancel 事件
      onCancel: () => {
        layer.close(popupIndex) // 关闭弹窗
      }
    })
  })
  app.mount(tempContainer) // 挂载到临时容器

  // 3. 调用 Layer 打开弹窗,内容为挂载后的 Vue 组件
  const popupIndex = layer.open({
    type: 1, // 1 = 页面层(非iframe)
    title: '自定义标题',
    area: ['500px', '300px'], // 弹窗宽高
    content: tempContainer, // 内容 = 挂载 Vue 组件的临时容器
    shade: 0.3, // 遮罩透明度
    fixed: true, // 固定弹窗位置
    // 弹窗关闭时触发(关键:卸载 Vue 组件 + 移除临时 DOM)
    cancel: () => {
      app.unmount() // 卸载 Vue 组件
      document.body.removeChild(tempContainer) // 移除临时容器
    }
  })

  // 额外保险:监听 Layer 关闭事件(防止手动关闭时未卸载)
  window.layer.on('close', (layero, index) => {
    if (index === popupIndex) {
      app.unmount()
      document.body.removeChild(tempContainer)
    }
  })
}
</script>

<style scoped>
.open-btn {
  padding: 8px 20px;
  border: none;
  border-radius: 4px;
  background: #1E9FFF;
  color: white;
  cursor: pointer;
  font-size: 14px;
}
</style>

四、优化:封装为通用 Hook(可复用)

将弹窗逻辑封装为 useLayerPopup Hook,方便多个组件调用:

javascript 复制代码
// hooks/useLayerPopup.js
import { createApp, h } from 'vue'

/**
 * 通用 Layer 弹窗 Hook,用于弹出 Vue 组件
 * @param {Component} component - 待弹出的 Vue 组件
 * @param {Object} options - 配置项
 * @param {Object} options.props - 组件 props 参数
 * @param {Object} options.layerOptions - Layer 弹窗配置(如 area、title 等)
 * @param {Function} options.onConfirm - 组件 confirm 事件回调
 * @param {Function} options.onCancel - 组件 cancel 事件回调
 * @returns {Object} - { close: 关闭弹窗的方法 }
 */
export const useLayerPopup = (component, options = {}) => {
  const {
    props = {},
    layerOptions = {},
    onConfirm = () => {},
    onCancel = () => {}
  } = options

  // 创建临时容器
  const tempContainer = document.createElement('div')
  document.body.appendChild(tempContainer)

  // 渲染组件并传递参数
  const app = createApp({
    render: () => h(component, {
      ...props,
      onConfirm: (data) => {
        onConfirm(data)
        closePopup()
      },
      onCancel: () => {
        onCancel()
        closePopup()
      }
    })
  })
  app.mount(tempContainer)

  // 打开 Layer 弹窗
  const popupIndex = window.layer.open({
    type: 1,
    area: ['500px', 'auto'], // 默认宽高
    title: 'Vue 组件弹窗',
    content: tempContainer,
    shade: 0.3,
    cancel: closePopup,
    ...layerOptions
  })

  // 关闭弹窗并清理资源
  const closePopup = () => {
    window.layer.close(popupIndex)
    app.unmount()
    document.body.removeChild(tempContainer)
  }

  // 监听 Layer 关闭事件
  window.layer.on('close', (layero, index) => {
    if (index === popupIndex) closePopup()
  })

  return { close: closePopup }
}
Hook 使用示例
vue 复制代码
<!-- ParentComponent.vue -->
<script setup>
import PopupComponent from './PopupComponent.vue'
import { useLayerPopup } from '@/hooks/useLayerPopup'

const openLayerPopup = () => {
  useLayerPopup(PopupComponent, {
    props: {
      title: 'Hook 封装的弹窗',
      content: '复用性更强的弹窗逻辑'
    },
    layerOptions: {
      area: ['600px', '350px'],
      title: '自定义弹窗标题'
    },
    onConfirm: (data) => {
      window.layer.msg(`Hook 接收确认数据:${data}`)
    },
    onCancel: () => {
      window.layer.msg('Hook 触发取消')
    }
  })
}
</script>

五、关键注意事项

  1. 组件卸载 :弹窗关闭时必须调用 app.unmount()removeChild(tempContainer),否则会导致内存泄漏;
  2. Layer 配置type: 1 表示页面层(非 iframe),content 直接传入 Vue 组件挂载的 DOM 容器;
  3. 通信方式 :通过 h 函数的 onXXX 传递事件回调,实现父组件与弹窗组件的通信;
  4. 样式隔离 :弹窗组件使用 scoped 样式,避免污染全局样式;
  5. Layer 版本:确保使用 Layui 2.5+ 版本,兼容 Vue3 的 DOM 操作。

通过以上步骤,即可在 Vue3 的 setup 语法中灵活使用 Layer 弹窗加载任意 Vue 组件,并支持组件通信和生命周期管理。

相关推荐
jingling5552 小时前
vue | 在 Vue 3 项目中集成高德地图(AMap)
前端·javascript·vue.js
J***Q2929 小时前
Vue数据可视化
前端·vue.js·信息可视化
JIngJaneIL9 小时前
社区互助|社区交易|基于springboot+vue的社区互助交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·社区互助
ttod_qzstudio10 小时前
深入理解 Vue 3 的 h 函数:构建动态 UI 的利器
前端·vue.js
_大龄10 小时前
前端解析excel
前端·excel
1***s63210 小时前
Vue图像处理开发
javascript·vue.js·ecmascript
一 乐11 小时前
应急知识学习|基于springboot+vue的应急知识学习系统(源码+数据库+文档)
数据库·vue.js·spring boot
一叶茶11 小时前
移动端平板打开的三种模式。
前端·javascript
前端大卫11 小时前
一文搞懂 Webpack 分包:async、initial 与 all 的区别【附源码】
前端