实现vue this调用的全局confirm组件
需求背景
在日常工作中用到vue开发就必然会涉及到组件,当然也会涉及到Tost、confirm等简单的全局组件。不管是在小程序、pc还是h5都会有。那么问题来了,在组件库及原生API没有支持到,但是我们又需要用的简单组件怎么办?一次又一次的引入注册?还是全局注册页面上写了一个又一个的空标签?这都不是我们想要的。所以我们要自己实习一个直接将组件挂载到this的原型链上的组件。例如:饿了么组件this.$confirm()、微信:wx.showTost()等等。本文仅针对Vue来实现一个全局组件。开撸!!!
说明
- JSX 本文实现vue全局组件是使用了 JSX 撸的组件。
- Vue.extend 通过 Vue.extend 进行组件创建。在本文不在做过多的赘述,如有不了解的请点击链接了解。
- Vue.$mount 通过Vue $mount挂载组件。同样不在做过多的赘述,请点击链接了解。
代码实现
1、实现页面元素内容
基础的组件内容实现就不做过多赘述,refs 不了解的请点击链接了解。
(1) 基本实现HTML+JS+CSS
// JS + HTML
import "@/test/test.css"
export default {
data(){
return {
visible:false,
confirmObj:{
title:"温馨提示",
content:"",
showCancel:true,
cancelText:"取消",
showConfirm:true,
confirmText:"确认",
}
}
},
methods:{
async open(data){
this.confirmObj = { ...this.confirmObj,...data }
this.visible = true
},
close(){
this.visible = false
},
handleClose(){
this.$emit("handleClose")
this.close()
},
handleConfirm(){
this.$emit("handleConfirm")
this.close()
}
},
render(){
let { title, content, showCancel, cancelText, showConfirm, confirmText } = this.confirmObj
let { visible, handleClose, handleConfirm } = this
return (
<div v-show={ visible } class="confirm-mask">
<div class="confirm-content">
<div class="confirm-title">{ title }</div>
<div class="confirm-mid">{ content }</div>
<div class="confirm-footer">
<button v-show={ showCancel } onClick={handleClose} >{ cancelText }</button>
<button class="confirm-footer-right" v-show={ showConfirm } onClick={handleConfirm} >{ confirmText }</button>
</div>
</div>
</div>
)
}
}
/* css */
.confirm-mask{
width: 100vw;
height: 100vh;
position: fixed;
background: rgba(0, 0, 0, 0.3);
left: 0;
top: 0;
bottom: 0;
right: 0;
display: flex;
align-items: center;
}
.confirm-content{
margin: 0 auto;
width: 400px;
height: 200px;
border-radius: 4px;
background: #fff;
}
.confirm-title{
text-align: left;
padding: 0px 10px;
height: 36px;
font-size: 16px;
display: flex;
align-items: center;
border-bottom: 1px solid #eee;
}
.confirm-mid{
display: flex;
align-items: center;
height: 123px;
}
.confirm-footer{
display: flex;
justify-content: flex-end;
cursor: pointer;
border: none;
padding: 5px 10px;
min-width: 80px;
border-top: 1px solid #eee;
}
.confirm-footer button{
background: #00AEBC;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
border: none;
border-radius: 4px;
min-width: 80px;
}
.confirm-footer-right{
margin-left: 10px;
}
(2) ref调用方式HTML+JS+CSS
export default{
data(){
return {}
},
methods:{
confirmTest(){
this.$refs["confirmTestComp"].open()
},
handleClose(){},
handleConfirm(){}
},
render(){
return (
<div class="div-body">
<h4 class="h-title">组件测试</h4>
<div class="div-content">
<el-button type="primary" onClick={this.confirmTest} >confirm组件测试按钮</el-button>
</div>
<ConfirmTestComp ref="confirmTestComp" @handleClose="handleClose" @handleConfirm="handleConfirm" />
</div>
)
}
}
/* css */
.h-title{
margin-top:20px;
padding: 10px 20px;
text-align: left;
background: #eee;
border-radius: 4px;
}
.div-body{
padding: 20px;
}
.div-content{
padding:20px 0;
display:flex;
}
2、实现组件ref调用及链式调用(Promise)
实现思路:按照饿了么的调用模式 this.$confirm().then().catch() 这种格式为启发,我第一反应就是Promise,new Promise((resolve,reject)=>{}).then(res=>{}).catch(err=>{}) 同样的链式调用结构。非常符合我们的要求,我们只需要稍微变通一下,请看下面新增代码。
export default {
data(){
return {
initPromise:null,
resolveFn:null,
rejectFn:null
}
},
methods:{
async open(data){
this.confirmObj = { ...this.confirmObj,...data }
this.visible = true
this.initPromise = await new Promise((res,rej)=> {
this.resolveFn = res
this.rejectFn = rej
})
return this.initPromise
}
}
}
组件使用
import "@/test/test.css"
import ConfirmTestComp from "@/test/test-confirm/testConfirmRender"
export default{
data(){
return {}
},
methods:{
confirmTest(){
this.$refs["confirmTestComp"].open().then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)
})
},
},
render(){
return (
<div class="div-body">
<h4 class="h-title">组件测试</h4>
<div class="div-content">
<el-button type="primary" onClick={this.confirmTest} >confirm组件测试按钮</el-button>
<ConfirmTestComp ref="confirmTestComp" />
</div>
)
}
}
3、实现组件挂载全局
- 单独起一个js文件创建组件并返回promise
// testConfirm.js
import Vue from "vue"
import TestConfirm from "./testConfirmRender"
let TestConfirmConstructor = Vue.extend(TestConfirm) //创建组件
let instance;
export default async function(options){
console.log("options",options);
instance = new TestConfirmConstructor()
console.log("instance",instance);
// 挂载组件
instance.$mount()
//将组件放到body下 这样是为了避免层级问题
document.body.appendChild(instance.$el)
return await instance.open(options)
}
- 全局挂载需要在main中挂载组件
//main.js
import TestConfirm from "@/test/test-confirm/testConfirm"
- 要实现全局挂载并通过this调用那么我们必须将组件open方法挂载到this的原型链上
// main
Vue.prototype.$testConfirm = TestConfirm
完整代码
- 组件
import "@/test/test.css"
export default {
data(){
return {
visible:false,
confirmObj:{
title:"温馨提示",
content:"",
showCancel:true,
cancelText:"取消",
showConfirm:true,
confirmText:"确认",
},
initPromise:null,
resolveFn:null,
rejectFn:null
}
},
methods:{
async open(data){
this.confirmObj = { ...this.confirmObj,...data }
this.visible = true
this.initPromise = await new Promise((res,rej)=> {
this.resolveFn = res
this.rejectFn = rej
})
return this.initPromise
},
close(){
this.visible = false
},
handleClose(){
this.rejectFn(" -handleClose- ")
this.close()
},
handleConfirm(){
this.resolveFn(" -handleConfirm- ")
this.close()
}
},
destroyed(){},
render(){
let { title, content, showCancel, cancelText, showConfirm, confirmText } = this.confirmObj
let { visible, handleClose, handleConfirm } = this
return (
<div v-show={ visible } class="confirm-mask">
<div class="confirm-content">
<div class="confirm-title">{ title }</div>
<div class="confirm-mid">{ content }</div>
<div class="confirm-footer">
<button v-show={ showCancel } onClick={handleClose} >{ cancelText }</button>
<button class="confirm-footer-right" v-show={ showConfirm } onClick={handleConfirm} >{ confirmText }</button>
</div>
</div>
</div>
)
}
}
- 组件处理文件
// testConfirm.js
import Vue from "vue"
import TestConfirm from "./testConfirmRender"
let TestConfirmConstructor = Vue.extend(TestConfirm)
let instance;
export default async function(options){
console.log("options",options);
instance = new TestConfirmConstructor()
console.log("instance",instance);
instance.$mount()
document.body.appendChild(instance.$el)
return await instance.open(options)
}
- main
// main
import TestConfirm from "@/test/test-confirm/testConfirm"
Vue.prototype.$testConfirm = TestConfirm
结语:代码粗糙,如有不合理、错误、冗余、不足的地方还请各位大佬及时指正。
佛祖保佑 永不宕机 永无BUG
/*
* _oo0oo_
* o8888888o
* 88" . "88
* (| -_- |)
* 0\ = /0
* ___/`---'\___
* .' \\| |// '.
* / \\||| : |||// \
* / _||||| -:- |||||- \
* | | \\\ - /// | |
* | \_| ''\---/'' |_/ |
* \ .-\__ '-' ___/-. /
* ___'. .' /--.--\ `. .'___
* ."" '< `.___\_<|>_/___.' >' "".
* | | : `- \`.;`\ _ /`;.`/ - ` : | |
* \ \ `_. \_ __\ /__ _/ .-` / /
* =====`-.____`.___ \_____/___.-`___.-'=====
* `=---='
*
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 佛祖保佑 永不宕机 永无BUG
*/