如何使用JS函数式调用方式将自定义组件并以弹窗形式展示?

在开发过程中,经常会用到弹窗,而有的弹窗可能是临时性的,其实不太想在template中创建一个临时性的弹窗组件,那么有没有比较好的方案处理呢?比如以函数式方式调用方式,其实是有的;

elementUI 2.x为例,MessageBox组件模拟了系统的alert,confirmprompt。其中prompt允许我们填入一个文本元素并给出了示例。

但有时候,我们并不总是需要文本输入,假设我们需要一个临时的时间选择弹窗怎么处理呢?

按照之前的我可能会无脑用Dialog组件写一个弹窗,里面放一个日期组件,但是这样会无形创造很多变量在datamethods

MessageBox组件给我们提供了一个函数式方式调用弹窗的方式,好处在于内容可以是VNode形式。贴出官网的调用方式

html 复制代码
<template>
  <el-button type="text" @click="open">点击打开 Message Box</el-button>
</template>

<script>
  export default {
    methods: {
      open() {
        const h = this.$createElement;
        this.$msgbox({
          title: '消息',
          message: h('p', null, [
            h('span', null, '内容可以是 '),
            h('i', { style: 'color: teal' }, 'VNode')
          ]),
          showCancelButton: true,
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          beforeClose: (action, instance, done) => {
            if (action === 'confirm') {
              instance.confirmButtonLoading = true;
              instance.confirmButtonText = '执行中...';
              setTimeout(() => {
                done();
                setTimeout(() => {
                  instance.confirmButtonLoading = false;
                }, 300);
              }, 3000);
            } else {
              done();
            }
          }
        }).then(action => {
          this.$message({
            type: 'info',
            message: 'action: ' + action
          });
        });
      }
    }
  }

其中message接收VNode参数,官网是通过Vue实例上提供的$createElement方法动态创建的,这种数据结构和webpack.vue文件打包后的数据结构是一致的。

Vue官网对其有说明: 渲染函数 & JSX

官网说createElement函数的第一个参数支持一个 HTML 标签名、组件选项对象,或者resolve 了上述任何一种的一个 async 函数。

既然这样,那我直接把组件el-date-picker扔进去不就好了吗。

csharp 复制代码
message: h('el-date-picker', null),

遗憾的是,弹窗可以正确的渲染出日期组件,由于该组件内部的原因,该组件在选择日期时出现了问题,不能正确执行。

经过我多种尝试,有一种方式可以实现我想要的效果。这里要用到平时开发用的比较少的方法,Vue.extendVue.compile

Vue.extend的用法

Vue.extend是用于创建一个"扩展实例构造器"的方法,可以基于基础Vue构造器来创建一个"子类"。通过Vue.extend返回的是一个组件构造器,可以用来挂载一个新的Vue实例。

js 复制代码
// 创建构造器
const CustomVue = Vue.extend({
  // 扩展选项
})

// 挂载实例
const vm = new CustomVue().$mount('#app')

或者手动挂载
const customVue = new CustomVue().$mount();
document.body.append(customVue.$el);

Vue.extend主要有以下几种使用场景:

  1. 创建可复用组件 :通过Vue.extend创建一个组件构造器,然后在其他组件中通过components选项注册和使用。
  2. 扩展现有组件:基于某个已有的组件构造器创建一个新的构造器,从而实现组件的扩展和继承。
  3. 动态渲染组件 :在运行时动态创建并渲染一个组件,比如通过Vue.extend创建一个组件构造器,然后通过new实例化并挂载。

Vue.compile的用法

Vue.compile是用于编译模板字符串的方法。它返回一个渲染函数render和静态节点的staticRenderFns。

js 复制代码
const { render, staticRenderFns } = Vue.compile(template)

Vue.compile 方法支持一个参数,即模板字符串。它会将模板字符串编译成一个 render 函数,并返回一个对象,包含 render 函数和静态节点的 staticRenderFns。些静态节点可以在组件渲染时进行缓存,提高渲染性。Vue.compile主要用于以下场景:

  1. 运行时编译模板字符串:当使用运行时构建的Vue时,模板需要在运行时编译,可以使用Vue.compile编译模板字符串。
  2. 扩展Vue编译器:Vue.compile暴露了编译模板的内部实现,可以基于此扩展编译器的功能,比如添加自定义指令等。

结合这两者的功能,我们就不用自己手动去编写VNode结构体了。

开发环境配置

默认我们在开发时,import Vue from 'vue';引入的只有运行时,想要使用的完整功能的话,需要在webpack中配置,引入带有编译引擎的版本,在vue.config.js文件中配置如下:

css 复制代码
configureWebpack: {
    resolve: {
        alias: {
            vue$: 'vue/dist/vue.esm.js',
        },
    },
},

根据自己的vuecli版本看具体怎么配置。

实战案例

下面我贴出示例代码参考

html 复制代码
<template>
	<div>
		<el-button @click="openModal" type="primary">打开弹窗</el-button>
	</div>
</template>
<script>
	import Vue from 'vue';
	export default {
		name: 'modal',
		components: {},
		props: {},
		data() {
			return {
				obj: {
					date: new Date()
				}
			};
		},
		computed: {},
		created() {},
		mounted() {},
		destroyed() {},
		methods: {
			openModal() {
				let pickerTime = new Date(); //定义一个变量用于存储选择的时间,先赋一个默认值
				const createElement = this.$createElement;
                                //手动创建一个Wrap组件,内容包含日期选择组件
				const Wrap = Vue.extend({
					template: `<el-date-picker placeholder="选择日期时间" type="datetime" v-model="value" @change="onChange"></el-date-picker>`,
					data() {
						return {
							value: pickerTime
						};
					},
					methods: {
						onChange(evt) {
							pickerTime = evt; //将组件内部的值赋值到外部变量
						}
					}
				});
				this.$options.components['wrap'] = Wrap; //将Wrap组件挂载到当前页面的局部组件中
				//对Wrap组件进行编译会返回一个包含render函数和静态函数staticRenderFns
				var compiled = Vue.compile(`<wrap/>`);
                                
                                
                           //唤起弹窗
				this.$msgbox({
					title: '消息',
					message: compiled.render.call(this, createElement), //调用编辑后组件的render函数,转化为虚拟dom
					showCancelButton: true,
					confirmButtonText: '确定',
					cancelButtonText: '取消',
					beforeClose: (action, instance, done) => {
						if (action === 'confirm') {
							instance.confirmButtonLoading = true;
							instance.confirmButtonText = '执行中...';
							setTimeout(() => {
								done();
								setTimeout(() => {
									console.log(pickerTime); //打印选择的时间
									instance.confirmButtonLoading = false;
								}, 300);
							}, 3000);
						} else {
							done();
						}
					}
				}).then(action => {
					this.$message({
						type: 'info',
						message: 'action: ' + action
					});
				});
			}
		}
	};
</script>
<style lang="scss" scoped></style>
<style scoped></style>

依此类推,我们就可以实现很多简单功能的交互弹窗了。

当然,如果上面的不满足的话,我们完全可以自己定制一个属于自己的函数式弹窗组件,下面列出示例代码:

html 复制代码
<template>
	<div>
		<el-button @click="openModal" type="primary">打开自定义弹窗</el-button>
	</div>
</template>
<script>
	import Vue from 'vue';
	export default {
		name: 'modal',
		components: {},
		props: {},
		data() {
			return {};
		},
		computed: {},
		created() {},
		mounted() {},
		destroyed() {},
		methods: {
			//自定义弹窗
			openModal() {
				// 定义一个扩展的 Vue 构造器
				var ModalComponent = Vue.extend({
					template: `
							<div v-show="isVisible" class="modal">
							<div class="modal-content">
								<div class="modal-header">
								<span class="close" @click="close">&times;</span>
								<h2>{{ title }}</h2>
								</div>
								<div class="modal-body">
								<p>{{ content }}</p>
								</div>
								<div class="modal-footer">
								<button @click="close">关闭</button>
								</div>
							</div>
							</div>
						`,
					props: ['title', 'content'],
					data: function () {
						return {
							isVisible: true, // 默认显示
						};
					},
					methods: {
						open: function () {
							this.isVisible = true;
						},
						close: function () {
							this.isVisible = false;
							this.$nextTick(() => {
								// 确保DOM已经更新
								if (this.$el && this.$el.parentNode) {
									this.$el.parentNode.removeChild(this.$el);
								}
								// 清理组件实例
								this.$destroy();
							});
						},
					},
				});
				// 使用 ModalComponent 创建 modal 实例
				var modalInstance = new ModalComponent({
					propsData: {
						title: 'Vue Modal',
						content: '这是一个使用 Vue.extend 创建的模态框。',
					},
				});

				// 挂载到新创建的 div 元素上,并添加到 body 中
				var mountNode = document.createElement('div');
				document.body.appendChild(mountNode);
				modalInstance.$mount(mountNode);
			},
		},
	};
</script>
<style lang="scss" scoped></style>
<style>
	.modal {
		position: fixed;
		z-index: 1000;
		left: 0;
		top: 0;
		width: 100%;
		height: 100%;
		overflow: auto;
		background-color: rgba(0, 0, 0, 0.4);
	}

	.modal-content {
		background-color: #fefefe;
		margin: 15% auto;
		padding: 20px;
		border: 1px solid #888;
		width: 80%;
	}

	.close {
		color: #aaa;
		float: right;
		font-size: 28px;
		font-weight: bold;
	}

	.close:hover,
	.close:focus {
		color: black;
		text-decoration: none;
		cursor: pointer;
	}

	.modal-header {
		padding-bottom: 10px;
		border-bottom: 1px solid #e5e5e5;
	}

	.modal-body {
		margin-top: 10px;
	}

	.modal-footer {
		padding-top: 10px;
		border-top: 1px solid #e5e5e5;
	}
</style>

创建实例时,传递参数使用了propsData,这是点击API:propsData查看

后面有好的想法再继续补充...

相关推荐
桂月二二21 分钟前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
CodeClimb1 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
沈梦研1 小时前
【Vscode】Vscode不能执行vue脚本的原因及解决方法
ide·vue.js·vscode
hunter2062062 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb2 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角2 小时前
CSS 颜色
前端·css
轻口味2 小时前
Vue.js 组件之间的通信模式
vue.js
浪浪山小白兔3 小时前
HTML5 新表单属性详解
前端·html·html5
lee5763 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579653 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter