在 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
二、核心原理
- 用
document.createElement创建临时 DOM 容器; - 用 Vue3 的
createApp+h函数创建组件实例并挂载到临时容器; - 调用
layer.open显示弹窗,内容指定为临时容器; - 弹窗关闭时,卸载 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>
五、关键注意事项
- 组件卸载 :弹窗关闭时必须调用
app.unmount()和removeChild(tempContainer),否则会导致内存泄漏; - Layer 配置 :
type: 1表示页面层(非 iframe),content直接传入 Vue 组件挂载的 DOM 容器; - 通信方式 :通过
h函数的onXXX传递事件回调,实现父组件与弹窗组件的通信; - 样式隔离 :弹窗组件使用
scoped样式,避免污染全局样式; - Layer 版本:确保使用 Layui 2.5+ 版本,兼容 Vue3 的 DOM 操作。
通过以上步骤,即可在 Vue3 的 setup 语法中灵活使用 Layer 弹窗加载任意 Vue 组件,并支持组件通信和生命周期管理。