Vue.extend封装函数式调用弹窗组件
问题背景:
平时我们使用组件都是import xxx from 'xxx';然后components里面注册,最后template中使用,这样一套流程下来略麻烦,有没有一种方式能像ElementUI的弹窗组件一样代码调用呢?下面看看如何实现
封装组件:
首先准备好组件和一个js文件用于导出调用组件的函数:
组件目录结构大概就是这样
然后我们需要在index.js中创建调用组件的函数,创建组件的代码就是继承一份原组件进行实例化然后挂载到叶面的节点中,这里我是挂载了一个弹窗组件,封装了一些默认props:
import emitter from '@/plugins/mitt';
import router from '@/router';
import { Message } from 'element-ui';
import Vue from 'vue';
import PayDialog from './pay-dialog.vue';
const PayDialogConstructor = Vue.extend(PayDialog); // 倒入组件然后先继承一份组件得到新的组件构造函数
/**
* @description 付费弹窗组件props校验
* @param {{
* url: string,
* trackingId?: number,
* onSuccess?: function,
* } | string} props
* @param {PromiseRejectionEvent} reject
* @param {Function} executeFunc
*/
const validateProps = (props, reject, executeInitComponent) => {
const propsTypes = ['string', 'object'];
const propsType = typeof props;
if (!propsTypes.includes(propsType)) {
return reject(
`Error in component PayDialog: invalid props type! expected ${propsTypes.join(
','
)}, got ${propsType}`
);
}
if (propsType === 'string' && props.trim() === '') {
return reject('Error in component PayDialog: url can not be empty string!');
}
if (propsType === 'object' && props.trackingId && !props.url) {
return reject(
'Error in component PayDialog: renewal must be have props trackingId with valid url!'
);
}
executeInitComponent();
};
/**
* @description 付费/续费弹窗组件
* 付费: 只需要传入url
* 续费: 需要同时传入url和trackingId 传了trackingId就代表是续费
* @param {{
* url: string,
* trackingId?: number,
* onSuccess?: function,
* } | string} options
*/
export default function (options) {
return new Promise((resolve, reject) => {
const executeInitComponent = () => {
const propsData = {
onDialogClosed(instance) {
document.querySelector('.app').removeChild(instance.$el); // 窗口完全关闭后销毁窗口
resolve(); // dosomething...
},
onSuccess: () => {
// 如果没传成功的回调就走默认的成功提示和页面跳转逻辑
if (!options.onSuccess) {
Message({
message: `${options.trackingId ? '续费' : '添加'}成功`,
type: 'success',
duration: 3000,
});
if (options.trackingId) {
return emitter.emit('reloadPage'); // 如果传了trackingId则是续费,刷新页面
}
return router.push('/safety'); // 如果没传trackingId就是添加,跳转至检测链接页面
}
options.onSuccess(); // 付费/续费成功自定义dosomething...
},
};
if (typeof options === 'string') {
propsData.url = options; // 如果是ASIN字符串 则为付费
} else {
Object.assign(propsData, {
trackingId: options.trackingId || 0, // 续费需要传商品的trackingId,
url: options.url, // asin/商品url
});
}
const payDialogInstance = new PayDialogConstructor({
propsData, // props的key叫做propsData 这个和普通组件不一样要注意
}); // 实例化组件
document
.querySelector('.app')
.appendChild(payDialogInstance.$mount().$el); // $mount()不传参就是将组件实例挂载到文档外,返回值就是实例本身,传选择器就是挂载到具体元素里
payDialogInstance.visible = true; // 组件挂载后显示出来
};
validateProps(options, reject, executeInitComponent); // 校验props
});
}
这样我们在使用时直接从index.js中导入函数调用即可:
楚楚梦厦: 感谢,很有用,真强!
williamyi74: 前人踩坑后人乘凉如果有帮助到你三连一下~
a42558444: 很棒,看了这个 一下就整明白了
williamyi74: 那用executeCommand('copy') 这个是所有都兼容
perfect*: firefix不支持ClipboardItem