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 组件,并支持组件通信和生命周期管理。

相关推荐
环境栈笔记7 分钟前
指纹浏览器检查代理失败怎么排查?先分清代理、网络和检测服务
前端·人工智能·后端·自动化
ShineWinsu12 分钟前
对于HTML:HTML常见标签的解析—下
前端·css·面试·html·标签·表单·emmet
恋猫de小郭13 分钟前
Kotlin 2.5 的 Companion 改造,对 KMP 的重要升级
android·前端·flutter
前端双越老师27 分钟前
程序员 1v1 访谈系列第二期:很焦虑但又不知道干啥
前端·ai编程·全栈
雪芽蓝域zzs34 分钟前
Webstorm打开uniapp uvue文件报红处理方案
vue.js
广州华水科技41 分钟前
北斗形变监测传感器在大坝安全监测中的应用与优势分析
前端
孔令飞1 小时前
如何学习Go
前端
HUIBUR科技1 小时前
AI 中枢,面向企业场景构建的 AI 智能体
前端·数据库·人工智能
随便写个软件1 小时前
【新版本】EasyExplorer资源管理器 3.12.0——地址栏重要更新
运维·服务器·前端·everything·资源管理器
Jack N1 小时前
【Strapi 系列之二】Collection / Single / Component 怎么选?Strapi 字段大全一篇讲透
前端·javascript·strapi