vue全局组件 之 自定义this 调用的全局element- confirm 组件实践

需求背景

在日常工作中用到vue开发就必然会涉及到组件,当然也会涉及到Toast、confirm等简单的全局组件。不管是在小程序、pc还是h5都会有。那么问题来了,在组件库及原生API没有支持到,但是我们又需要用的简单组件怎么办?一次又一次的引入注册?还是全局注册页面上写了一个又一个的空标签?这都不是我们想要的。所以我们要自己实现一个直接将组件挂载到this的原型链上的组件。例如:饿了么组件this.$confirm()、微信:wx.showToast()等等。本文仅针对Vue来实现一个全局组件。开撸!!!

说明

  1. JSX 本文实现vue全局组件是使用了 JSX 撸的组件。
  2. Vue.extend 通过 Vue.extend 进行组件创建。在本文不在做过多的赘述,如有不了解的请点击链接了解。
  3. 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、实现组件挂载全局

  1. 单独起一个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)
	}
  1. 全局挂载需要在main中挂载组件
//main.js
import TestConfirm from "@/test/test-confirm/testConfirm"
  1. 要实现全局挂载并通过this调用那么我们必须将组件open方法挂载到this的原型链上
	// main
	Vue.prototype.$testConfirm = TestConfirm

完整代码

  1. 组件
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>
        )
    }
}
  1. 组件处理文件
	// 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)
	}
  1. main
	// main
	import TestConfirm from "@/test/test-confirm/testConfirm"
	Vue.prototype.$testConfirm = TestConfirm

结语:代码粗糙,如有不合理、错误、冗余、不足的地方还请各位大佬及时指正。


佛祖保佑 永不宕机 永无BUG

	/*
 *                        _oo0oo_
 *                       o8888888o
 *                       88" . "88
 *                       (| -_- |)
 *                       0\  =  /0
 *                     ___/`---'\___
 *                   .' \\|     |// '.
 *                  / \\|||  :  |||// \
 *                 / _||||| -:- |||||- \
 *                |   | \\\  - /// |   |
 *                | \_|  ''\---/''  |_/ |
 *                \  .-\__  '-'  ___/-. /
 *              ___'. .'  /--.--\  `. .'___
 *           ."" '<  `.___\_<|>_/___.' >' "".
 *          | | :  `- \`.;`\ _ /`;.`/ - ` : | |
 *          \  \ `_.   \_ __\ /__ _/   .-` /  /
 *      =====`-.____`.___ \_____/___.-`___.-'=====
 *                        `=---='
 * 
 * 
 *      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * 
 *            佛祖保佑       永不宕机     永无BUG
 */
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白开水丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值