学完即用之Vue.extend+$mount改造dialog实践__Vue.js
发布于 3 年前 作者 banyungong 1331 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

痛点

当我们在单个页面中存在多个弹窗组件时,一般我们会对其进行封装处理,而此时页面会出现多个组件块,对于其组件命名、传参命名、事件命名等往往会显得冗长且让人头疼,而且当多个页面调用同一个组件时会产生多余的重复代码。对于我而言,更习惯使用js去解决不必要html,因此在此基础上,想到了擅长使用在独立组件开发场景中的Vue.extend+$mount

初步尝试

首先我们弄清楚我们想要到达的效果是什么样的,既然是点击调用弹窗,那么我们需要在点击的时候通过js将组件实例化并将其渲染挂载到body节点上。

将我们要使用的弹窗组件封装成DialogA.vue(此处以elementUI为例)

<template>
    <el-dialog 
        title="测试弹窗"
        :visible="dialogABool"
    >
        <div class="content">
            <!-- todo -->
        </div>
    </el-dialog>
</template>

<script>
import { Component, Vue } from 'vue-property-decorator'
@Component({})
export default class DialogA extends Vue {
    dialogABool = false
}
</script>

在其同级目录下新建index.js用来实例挂载

import Vue from 'vue';
import dialogA from './DialogA';
const DialogA = Vue.extend(dialogA);
var instance = null;

export function alertDialogA(){
    if(!instance){
        instance = new DialogA();
        instance.$mount();
        document.body.appendChild(instance.$el);
    }
    instance.dialogABool = true;
}

这样我们就可以直接在其他组件中通过调用alertDialogA()来控制弹窗组件。

传参与回调

传参比较简单,既然是一个方法自然可以接收参数alertDialogA(params),因为extend创建的Vue构造器继承了DialogA组件的属性,方法等,因此在index.js中将参数赋值给DialogA中变量即可。

而想要接收回调,我们第一时间想到使用Promise来完成,将index.js改造一下:

export function alertDialogA(params){
    if(!instance){
        instance = new DialogA();
        instance.$mount();
        document.body.appendChild(instance.$el);
    }
    instance.dialogABool = true;
    instance.params = params;
    return new Promise((resolve,reject)=>{
        instance.promise = {
            resolve,reject
        }
    })
}

在DialogA中通过调用this.promise.resolve()来触发‘完成动作’,在调用弹窗组件中通过then方法实现回调,即alertDialogA(params).then(()=>{}),当然此处也可以传递回调参数使得组件使用起来更加灵活。

运用思考

可以看见我们在index.js中将var instance = null;放在了alertDialogA方法外部,并且在方法里面进行了非空判断,这也就意味着只有在页面首次加载的时候才会渲染并加载弹窗组件,这样当我们进行多次关闭-打开弹窗组件操作时,组件内的变量存在被污染的情况,此时我们可以内置一个钩子函数init()来初始化数据,从而使组件在不重新渲染的条件下可以正常使用。

当我们需要每次打开弹窗都需要重新渲染时,一来可以将var instance = null;放在方法内部调用并放开判断,二来可以通过在DialogA.vue中添加v-if来达到此效果。

完整代码

DialogA.vue

<template>
    <el-dialog 
        title="测试弹窗"
        :visible="dialogABool"
    >
        <div class="content">
            <!-- todo -->
        </div>
        <span slot="footer" class="dialog-footer">
            <el-button type="primary" @click="onSubmit">确定</el-button>
        </span>
    </el-dialog>
</template>

<script>
import { Component, Vue } from 'vue-property-decorator'
@Component({})
export default class DialogA extends Vue {
    dialogABool = false
    params = null

    init() {
        // 初始化数据
    }
    onSubmit() {
        // todo
        this.promise.resolve(data)
    }
}
</script>

index.js

import Vue from 'vue';
import dialogA from './DialogA';
const DialogA = Vue.extend(dialogA);
var instance = null;

export function alertDialogA(params){
    if(!instance){
        instance = new DialogA();
        instance.$mount();
        document.body.appendChild(instance.$el);
    }
    instance.dialogABool = true;
    instance.params = params
    // 获取页面数据
    instance.init();
    return new Promise((resolve,reject)=>{
        instance.promise = {
            resolve,reject
        }
    })
}

调用弹窗组件页面

alertDialogA({name:'测试数据'}).then((data)=>{console.log(data)})

总结比较

熟悉elementUI的人不难发现,这种用法和轻量级的弹出层this.$confirm()this.$alert()this.$message()等用法类似,方便的同时也满足了弹出复杂内容的要求,可谓是一举两得。

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: dongfangyiyu 原文链接:https://juejin.im/post/6959449865470345252

回到顶部