背景
公司已经在一些项目上迁移vue3了,由于设计稿的弹窗组件和之前的vue2项目的比较一致,就直接拿过来改了,这里记录一下碰到的一些问题。
插件的定义
首先需要我们了解下插件在vue中的说明
- 插件用于向Vue添加全局功能
- 一般是有着install方法作为注册入口的Object或者function
- install提供两个参数,第一个是app,第二个options
插件实现的能力一般分为以下几种
- 添加全局方法或者属性
- 添加全局资源:指令/过渡等
- 通过插件(使用全局 mixin 方法),添加一些组件选项
- 添加全局实例方法,通过把它们添加到
config.globalProperties上实现 - 一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如 vue-router
常见的全局注册vue2和vue3的区别
| 实现方式 | vue2 | vue3 |
|---|---|---|
| 全局实例方法 | Vue.prototype | app.config.globalProperties |
| 全局组件 | Vue.component | app.component |
| 全局指令 | Vue.directive | app.directive |
vue2中插件的写法
main.js
import Vue from "vue";
import App from "./App.vue";
import confirm from "./···/confirm/index";
// 通过Vue.use()装载插件
Vue.use(confirm);
new Vue({
render: (h) => h(App),
}).$mount("#app");
/components/confirm/index.js
import Comfirm from "./ComfirmModal.vue";
const ComfirmPlugin = {};
/**
* 在main.js中Vue.use时会自动传入vue实例并执行install
*/
ComfirmPlugin.install = function (Vue) {
//1、Vue.extend拿到构造器constructor
const pluginConstructor = Vue.extend(Comfirm);
//2、创建组件对象
const ComfirmPlugin = new pluginConstructor();
//3、将组件对象,手动挂载到某一个元素上
ComfirmPlugin.$mount(document.createElement("div"));
// 其中3、4也可以合并为下面代码,在创建的时候就指定挂载元素
// const plugin = new pluginConstructor({
// el: document.createElement("div"),
// data: {},
// });
document.body.appendChild(ComfirmPlugin.$el);
//5、挂载在 Vue.prototype中实现全局访问
Vue.prototype.$confirm = ComfirmPlugin;
};
export default ComfirmPlugin; //将ComfirmPlugin导出
vue3中插件的写法
main.js
import { createApp } from "vue";
import App from "./App.vue";
import Confirm from "./components/confirm";
// 通过createApp(App).use()装载插件
const app = createApp(App).use(Confirm);
app.mount("#app");
/components/confirm/index.js
import Comfirm from "./ComfirmModal.vue";
import { createApp } from "vue";
const ComfirmPlugin = {};
ComfirmPlugin.install = function (app) {
//1、实例化并绑定组件
const plugin = createApp(Comfirm);
const instance = plugin.mount(document.createElement("div"));
//2.将挂载的Node添加到body中
document.body.appendChild(instance.$el);
//3、定义全局
app.config.globalProperties.$confirm = instance;
};
export default ComfirmPlugin;
组件代码
目前想要实现的需求有:
- 可以直接将组件导入页面,通过父页面传入数据控制
- 直接可以通过全局调用
这里附上最终实现的代码,其中由于之前的组件实现中用到了$on和$off。查了官方文档,这两个方法在vue3中已被移除,所以改造中也去掉了这两个方法。最终在博客代码有所删改,有问题可以评论区交流
ConfirmModal.vue
<template>
<div class="confirm-modal">
<transition name="fade">
<!-- @touchmove.prevent -->
<div class="modal-dialog" @click="clickMaskToClose ? handleCancel() : null" v-if="visible" @touchmove.prevent>
<div class="modal">
<div class="modal-title" v-if="title">
{{ title }}
</div>
<div :class="['modal-content', title ? '' : 'no-title-content']">
{{ content }}
</div>
<div class="split-line-top"></div>
<div class="modal-footer">
<div class="btn-cancel" @click="handleCancel" v-if="showCancelButton">
{{ cancelText }}
</div>
<div class="split-line-center" v-if="showCancelButton"></div>
<div class="btn-confirm" :style="{ color: confirmColor }" @click="handleConfirm">
<div>{{ confirmText }}</div>
</div>
</div>
</div>
</div>
</transition>
</div>
</template>
<script>
import { ref, defineComponent, reactive, toRefs } from "vue";
export default defineComponent({
props: {
visible: {
type: Boolean,
default: false,
},
showCancelButton: {
type: Boolean,
default: false,
},
showConfirmButton: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "提示",
},
content: {
type: String,
default: "内容",
},
confirmText: {
type: String,
default: "我知道了",
}, // 确认按钮文本
confirmColor: {
type: String,
default: "#409EFF",
},
cancelText: {
type: String,
default: "取消",
}, // 取消按钮文本
clickMaskToClose: {
type: Boolean,
default: false,
}, // 点击遮罩是否隐藏
},
emits: {
onConfirm: null,
onCancel: null,
},
setup(props, context) {
let tempData = Object.assign({}, props);
const propsData = reactive(tempData);
const handleConfirm = () => {
propsData.visible = false;
context.emit("onConfirm");
};
const handleCancel = () => {
propsData.visible = false;
context.emit("onCancel");
};
return {
...toRefs(propsData),
handleCancel,
handleConfirm,
};
},
});
</script>
<style scoped>
@import "../assets/css/animate.css";
.modal-dialog {
width: 100%;
height: 100%;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
text-align: center;
z-index: 999;
transform: translateZ(9999px);
letter-spacing: 0;
background: rgba(0, 0, 0, 0.3);
}
.modal {
position: absolute;
top: 40%;
left: 50%;
z-index: 1000;
width: 300px;
transform: translate(-50%, -50%);
box-sizing: border-box;
background: #fff;
border-radius: 4px;
}
.modal-title {
padding: 24px 28px 0 28px;
font-size: 18px;
line-height: 25px;
color: #030303;
}
.modal-content {
font-size: 16px;
line-height: 21px;
color: #5e5f64;
padding: 16px 24px 24px 24px;
}
.no-title-content {
font-size: 16px;
padding: 28px;
color: #333333;
}
.modal-right {
padding-right: 10px;
width: 36px;
background: #f2f2f2;
color: rgba(0, 16, 38, 0.3);
font-size: 12px;
border-radius: 0 4px 4px 0;
position: absolute;
top: 0;
right: 0;
bottom: 0;
}
.split-line-top {
height: 1px;
transform: scale(1, 0.5);
background: #e8eaef;
}
.modal-footer {
width: 100%;
display: flex;
align-items: center;
height: 52px;
font-size: 16px;
line-height: 52px;
text-align: center;
}
.split-line-center {
width: 1px;
height: 100%;
transform: scale(0.5, 1);
background: #e8eaef;
}
.btn-cancel {
flex: 1;
color: #696d76;
}
.btn-confirm {
position: relative;
flex: 1;
color: #409eff;
}
</style>
index.js
import { createApp } from "vue";
import ConfirmComponent from "./ConfirmModal.vue";
const ConfirmPlugin = {};
let $vm;
const defaultsOptions = {
title: "提示",
content: "内容",
confirmText: "确定",
cancelText: "取消",
confirmColor: "#409EFF",
showCancelButton: true,
showConfirmButton: true,
clickMaskToClose: false,
};
const initInstance = () => {
const app = createApp(ConfirmComponent);
const container = document.createElement("div");
$vm = app.mount(container);
document.body.appendChild(container);
};
ConfirmPlugin.install = function (app) {
const confirm = {
show(options) {
if (!$vm) initInstance();
options = Object.assign({}, defaultsOptions, options);
for (const i in options) {
$vm[i] = options[i];
}
let handleCancel = $vm.handleCancel;
let handleConfirm = $vm.handleConfirm;
$vm.handleCancel = () => {
handleCancel();
options && options.onCancel && options.onCancel();
};
$vm.handleConfirm = () => {
handleConfirm();
options && options.onConfirm && options.onConfirm();
};
$vm.visible = true;
return $vm;
},
hide() {
if ($vm) $vm.visible = false;
},
};
app.config.globalProperties.$confirm = confirm;
};
export default ConfirmPlugin;
main.js
import { createApp } from "vue";
import App from "./App.vue";
import Confirm from "./components/confirm";
// 通过createApp(App).use()装载插件
const app = createApp(App).use(Confirm);
app.mount("#app");
最后调用的代码及效果如下
setup() {
const { proxy } = getCurrentInstance();
proxy.$confirm.show({
title: "温馨提示",
onConfirm: () => {
console.log("confirm", proxy.$confirm);
},
});
},