声明式组件是Vue中常见的组件,声明式组件通过在模板中通过添加条件来判断组件的显示和隐藏,非常方便。但是有些时候我也需要命令式的组件。比如说一些全局的弹框,通知,提示等,使用命令式组件可能会更加方便。
什么是命令式组件呢?
命令式组件,又称为程序化组件,指的是通过 JavaScript 代码直接控制组件的创建与销毁,通常用于需要动态生成或在全局范围内操作的组件,如弹窗、通知、提示框等。这种方式提供了更高的灵活性和控制力,适用于复杂或需要在多处调用的组件。
Vue2中如何开发一个命令式组件?
在一些第三方库中就有命令式组件,比如在element-ui中Message 消息提示,MessageBox弹框,Notification 通知 都是命令式组件。今天我们就使用Vue2来开发一个MessageBox命令式组件。
-
创建组件文件夹和文件:在
components目录下创建MessageBox文件夹,并在其中创建index.js和MessageBox.vue文件。 -
编写 MessageBox.vue:此文件定义了组件的模板、样式和逻辑。组件接收标题、内容、确认和取消按钮的文本作为属性,并提供确认和关闭的方法。
<template>
<div class="message-box-wraper">
<div class="message-box">
<div class="header">
<h3>{{ title }}</h3>
<span @click="$messageBox.close()" class="close">X</span>
</div>
<div class="content">
{{ content }}
</div>
<div class="footer">
<button class="btn detault" @click="$messageBox.close()">取消</button>
<button class="btn primary" @click="onConfirm">确认</button>
</div>
</div>
</div>
</template>
<script lang="js">
export default {
name: 'MessageBox',
props: {
title: { // 标题
type: String,
default: '消息确认框'
},
content: { // 弹框内容
type: String,
default: '这是消息提示内容'
},
confirm: { // 组件内部的index.js传过来,不由用户传
type: Function,
default () {
}
},
confirmButtonText: { // 确认按钮的文案
type: String,
default: '确定',
},
cancelButtonText: { // 取消按钮的文案
type: String,
default: '取消'
}
},
methods: {
onConfirm () {
this.confirm()
}
}
}
</script>
<style lang="scss">
h3,
p{
margin: 0;
padding: 0;
}
.message-box-wraper{
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
position: fixed;
z-index: 99;
.message-box{
width: 420px;
height: 200px;
border-radius: 4px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: #fff;
padding: 30px;
box-sizing: border-box;
.header{
display: flex;
justify-content: space-between;
.close {
cursor: pointer;
}
}
.content {
margin: 20px 0;
}
.footer {
display: flex;
justify-content: flex-end;
.btn {
padding: 9px 15px;
background: #fff;
border: 1px solid #c6e2ff;
border-radius: 4px;
cursor: pointer;
&.primary {
background: #66b1ff;
border-color: #66b1ff;
margin-left: 10px;
color: #fff;
}
}
}
}
}
</style>
- 编写 index.js:此文件定义了插件的安装方法。通过 Vue.extend 创建组件实例,并在 Vue.prototype 上添加 open 和 close 方法,以便在全局范围内调用。
import _MessageBox from "./MessaeBox.vue";
export default {
install(Vue) {
let messageBox = null;
Vue.component(_MessageBox.name, _MessageBox);
Vue.prototype.$messageBox = {
open(content, title, otherConfig) {
if (!messageBox) {
const _this = this;
return new Promise((resolve) => {
const confirm = () => {
resolve();
_this.close();
};
const MessaeBox = Vue.extend({
render(h) {
return h("MessageBox", {
props: {
content: content,
title: title,
confirm,
...otherConfig,
},
});
},
});
messageBox = new MessaeBox();
_this.vm = messageBox.$mount();
document.body.appendChild(_this.vm.$el);
});
}
},
close() {
document.body.removeChild(this.vm.$el);
messageBox.$destroy();
messageBox = null;
this.vm = null;
},
};
},
};
关键技术点:
- install Vue插件的写法,用的时候直接Vue.use()就可以使用了
- 在Vue.prototype添加open方法和close方法,这样我们就可以直接在使用MessageBox的地方直接调用显示的方法和关闭的方法
- 使用Vue.extend扩展一个Vue组件构造器。
- 返回Promise ,点击确认的时候在确认的时候在确定状态。
- 使用render函数的h参数函数进行组件渲染。
- destroy()销毁组件实例
- 在 main.js 中注册插件:使用
Vue.use()方法将 MessageBox 注册为全局插件。
import Vue from "vue";
import App from "./App.vue";
// 导入MessageBox
import MessageBox from "./components/MessageBox";
Vue.config.productionTip = false;
// 全局使用MessageBox
Vue.use(MessageBox);
new Vue({
render: (h) => h(App),
}).$mount("#app");
- 在应用中使用:通过
this.$messageBox.open()调用组件,并处理组件的确认事件。
<template>
<div id="app">
</div>
</template>
<script>
export default {
mounted () {
this.$messageBox.open('新的弹框内容', '新的弹框标题', {
confirmButtonText: '确定',
cancelButtonText: '取消',
}).then(() => {
console.log('点击了确认')
})
}
}
</script>
<style lang="scss">
</style>
查看效果:
点击确认,就能看到点击了确认。就这样一个Vue2开发的命令式消息弹框组件就完成了。
Vue3 如何开发一个命令式组件
Element-plus 是基于Vue3.js开发的,MessageBox 同样使用的是命令式组件开发方式。接下来就来看看Vue3.js 中怎么开发一个Element-plus 中那样的MessageBox 命令式组件。
-
创建组件文件夹和文件:与 Vue2 相同,在
components目录下创建MessageBox文件夹,并在其中创建index.js和MessageBox.vue文件。 -
编写 MessageBox.vue:同样定义组件的模板、样式和逻辑。与 Vue2 不同的是,Vue3 使用 Composition API 的
defineProps和setup函数来处理组件的属性和方法。
<template>
<div class="message-box-wraper">
<div class="message-box">
<div class="header">
<h3>{{ title }}</h3>
<span @click="$messageBox.close()" class="close">X</span>
</div>
<div class="content">
{{ content }}
</div>
<div class="footer">
<button class="btn detault" @click="close">{{ cancelButtonText }}</button>
<button class="btn primary" @click="confirm">{{confirmButtonText}}</button>
</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
title: { // 标题
type: String,
default: '消息确认框'
},
content: { // 弹框内容
type: String,
default: '这是消息提示内容'
},
confirmButtonText: { // 确认按钮的文案
type: String,
default: '确定',
},
cancelButtonText: { // 取消按钮的文案
type: String,
default: '取消'
},
confirm: {
type: Function,
default () {
}
},
close: {
type: Function,
default () {}
}
})
</script>
<style lang="scss">
h3,
p{
margin: 0;
padding: 0;
}
.message-box-wraper{
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
position: fixed;
z-index: 99;
.message-box{
width: 420px;
height: 200px;
border-radius: 4px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: #fff;
padding: 30px;
box-sizing: border-box;
.header{
display: flex;
justify-content: space-between;
.close {
cursor: pointer;
}
}
.content {
margin: 20px 0;
}
.footer {
display: flex;
justify-content: flex-end;
.btn {
padding: 9px 15px;
background: #fff;
border: 1px solid #c6e2ff;
border-radius: 4px;
cursor: pointer;
&.primary {
background: #66b1ff;
border-color: #66b1ff;
margin-left: 10px;
color: #fff;
}
}
}
}
}
</style>
- 编写 index.js:使用 Vue3 的 createApp 方法创建组件实例。通过 h 函数渲染组件,并提供确认和关闭的回调函数。在组件销毁时,清理 DOM 节点以释放资源。
import { createApp, h } from "vue";
import _MessageBox from "./MessageBox.vue";
function showMessageBox(options) {
return new Promise((resolve, reject) => {
const container = document.createElement("div");
document.body.appendChild(container);
const confirm = () => {
resolve();
clearUp();
};
const close = () => {
reject();
clearUp();
};
const app = createApp({
render() {
return h(_MessageBox, {
...options,
confirm,
close,
});
},
});
app.mount(container);
function clearUp() {
app.unmount();
if (container.parentNode) {
container.parentNode.removeChild(container);
}
}
});
}
const MessageBox = {
confirm(content, title, options) {
return showMessageBox({
content,
title,
...options,
});
},
};
export default MessageBox;
- 在组件中使用:通过
MessageBox.confirm()方法调用组件,并处理组件的确认和关闭事件。
Vue3 的实现方式更为简洁和现代化,得益于 Vue3 的 Composition API 和 createApp 方法。它简化了组件的创建和管理,使得命令式组件的开发更加直观。
<script setup>
import MessageBox from './components/MessageBox/index.js';
MessageBox.confirm('这是一段新的提示内容', '新的提示标题', {
confirmButtonText: '确定',
cancelButtonText: '取消',
}).then(() => {
console.log('点击了确定')
}, () => {
console.log('点击了取消或关闭')
})
</script>
<template>
<div>
</div>
</template>
<style scoped>
</style>
可以看到Vue3 和Vue2开发命令式组件最大的区别就在于Vue2采用Vue.extend构建组件,Vue3采用createApp构造组件。
总结
命令式组件在 Vue.js 中提供了灵活的组件控制方式,适用于需要动态生成和全局调用的场景。通过本文的介绍,我们了解了在 Vue2 和 Vue3 中实现命令式组件的不同方法。无论是使用 Vue2 的 Vue.extend 还是 Vue3 的 createApp,命令式组件都为开发者提供了强大的工具,帮助他们在复杂的应用场景中实现更高效的组件管理。随着 Vue.js 的不断发展,命令式组件的实现方式也将继续演变,为开发者提供更为强大和灵活的解决方案。