关于我实现vue 函数式弹窗 把项目搞溢出了

2,447 阅读4分钟

vue函数式弹窗

之前就看了很多文章了 一直想做一个自己的,主要还是基于二次封装 和 Vue.extend 实现 我会分享一下当时踩的坑 然后如何处理的

主要完成功能如下:

  1. 支持title
  2. 加载子组件渲染
  3. 提供通信机制回调
  4. Promise 化

心路历程

我:今天我要搞一个 showDialog 函数弹窗

疯狂调研(各种百度),1个小时后... 我靠 好像没有我想要的,翻翻 api Vue.extend 创建子类 好 就用你了

一顿操作 显示 关闭 showDialog 已经实现 .... 但是 加载子组件怎么办? 我要传递一个path 然后使用 import(path) 和 动态组件 挂载吗?

showDialog({
    title:'标题'
    path: 'demo' 
})

有了思绪 干 卧槽报错 import 不支持 动态变量 path 是 undefined,哦哦哦 还有require() 可以用 ,诶,这下可以动态加载子组件了 测试 点吧 点吧 提交 commit -m 'feat 完成函数式弹窗'

第二天

主管:为什么我 uni 项目跑不动啦?

我:会心一笑 我也启动(显然不知道是我引出的问题呢!) 什么情况 node 堆栈溢出 ,之前没问题 我提交了 函数式弹窗之后就不行了 我靠,删掉我的代码 ,可以启动了

我的内心:什么垃圾性能

我:告诉大家 好像是弹窗的问题 我把他从全局注册下了 你们更新代码

晚上

内心:为啥要报错,于是开始复盘,想起以前有一篇文章require()import() 的区别 大致是加载同步 和 异步 ,import() 只有在调用的时候去加载相关的内容 而且有惰性 在router中我们使用路由懒加载也是因为import() 这个原因,require() 就不同了 他会一开始就去同步加载内容 uni 启动不了 我去同步加载组件导致相关依赖都要被解析 导致溢出问题 这也是一开始我不打算使用 require() 的原因 ,当然 require()去拿一些图片加载的时候还是很常用的 ,这是因为他同步加载的特性所致,然后分析 uni 编译的报错了 webpack 相关,我尝试去配置require()按需引用配置 但是 我好像没有成功 ,所以 还是说明一下 使用 import(path) 为啥会失败,因为 path设置的一个变量 webpack4 属于是静态的 path 就导致解析不出来,综上的问题,就可以想出方案了,我不使用path 我传递一个 uiLoader 函数也就是 ()=>import('demo') 很像路由懒加载 不 他就是,经过调试 最终这个确实可行

经过一番折腾,给大家看看是如何实现的,基于uview u-modal的二次封装

实现

肯定还是要有vue文件的

xxx-modal.vue

<template>
	<view>
		<u-modal 
        :show-title="false" 
        @confirm="confirm" 
        :title="title" 
        v-model="model.visible" 
        :show-cancel-button="true" 
        :show-confirm-button="true"
		>
			<component :is="component" v-if="component"></component>
		</u-modal>
	</view>
</template>

<script>
	export default {
		name: 'Tsl-Dialog',
		props: {
			uiLoader: {
				type: Function,
			},
			title: {
				type: String,
			},
			onClose: {
				type: Function,
			},
			onSuccess: {
				type: Function,
			},
		},
		data() {
			return {
				component: null,
				model: {
					visible: false,
				},
				emitter: null,
			};
		},
		methods: {
			async on() {
				this.model.visible = true;

				this.uiLoader().then(() => {
					this.component = this.uiLoader;
				});
			},
			confirm() {
				// 收集数据
				this.onSuccess(this.emitter);
			},
			cancel() {
				this.onClose();
			},
		},
		created() {},
		mounted() {
			// 不能使用 import() uni编译应该是使用的是webpack4 不支持变量传入
			uni.$on('POST_ACTION', (v) => {
				this.emitter = v;
			});
		},
		beforeDestroy() {
			uni.$off('POST_ACTION');
		},
	};
</script>

<style lang="scss" scoped></style>

说明:

on 的时候 就通过 执行 uiLoadeer把 component 挂载上 通信还是使用 bus 因为要 Promise 化的原因 所以 我内部组件 接收 POST_ACTION 存放的信息 然后执行回调

showDialog.js

// 因为是最终版本 有些参数 可以忽略
import Modal from './modal.vue';
import Vue from 'vue';

const Instance = Vue.extend(TslModal);

function showDialog(props) {
	const { title, uiLoader, content, showButton = false, borderRadius = 30, confirmColor = '#2979ff', cancelColor = '#606266' } = props;

	return new Promise((resolve, reject) => {
		const dialogContext = new Instance();

		// 挂载加载器
		dialogContext.uiLoader = uiLoader;
		// 挂载props
		dialogContext.title = title;
		dialogContext.content = content;
		dialogContext.borderRadius = borderRadius;
		dialogContext.confirmColor = confirmColor;
		dialogContext.cancelColor = cancelColor;
		if (showButton) {
			dialogContext.showConfirmButton = true;
			dialogContext.showCancelButton = true;
		}
		// 打开弹窗
		dialogContext.on();
		// 成功按钮回调
		dialogContext.onSuccess = function (v) {
			props.onSuccess && props.onSuccess(v);
			resolve(v);
			remove();
		};
		// 挂载dom
		document.body.appendChild(dialogContext.$mount().$el);
		// 取消事件
		dialogContext.cancel = function () {
			dialogContext.close();
			remove();
		};
		// 移除挂载dom
		function remove() {
			document.body.removeChild(dialogContext.$mount().$el);
		}
	});
}

export default showDialog;

说明:

通过 Vue.extend()modal 组件传入 然后构造一个实例 这里对内部源码不是太熟都是通过一个个添加上去,使用 Promise 包裹 弹窗我们底部有确认取消按钮 当点击确认的时候 就把 resolve(v) 执行 把相对应的数据发出去,关闭的时候 记得移除 dom 顺序都是从上往下的 我们手动调用 on 同时打开弹窗 和 加载组件 因为一开始 我们在最上面就挂载了 uiLoader 所以 on 执行的时候就已经有啦 最后 导出 可以使用导入 也可以 挂载原型

使用方法

import showDialog from '@/components/modal/showDialog';

showDialog({
    title: '我是弹窗',
    showButton: true,
    borderRadius: 0,
    content: '123',
    uiLoader: () => import('./demo.vue'), demo 中 uni.$emit('POST_ACTION', 'value');
    onSuccess: (res) => {
    console.log('postaction1', res); // value
},
}).then((res) => {
    console.log('postaction', res); // value
});

content 还是支持了 和uiLoader优先级的话我在内部做了判断

结语:

作为一个修bug工程师,这次实现中认知到自己的知识面还是不够,也是通过这个问题,慢慢分析最后处理掉了,其实过程还是有点困难的,最近不是就业寒冬吗?希望大家还是能够放好心态,始终让自己保持优势,我比较菜,只能每天了解一点,如果我在同龄水平中能超越50%,就满足了,知识储备还是很重要的,就大概多准备比目前薪酬多几k的量吧,这样你才会有竞争力,最后送大家一句话:总有人间一两风,填我十万八千梦