一个拓展性极强的el-dialog弹窗的插件解决方案

目的,其实为了用js的方式调用弹窗

插件的目的就的为了已js的方式调用弹出组件。自定义内容。无需关注弹窗使用中的代码。无需写弹窗相关的html代码。
就这样,轻轻松松~~ 就完成了一个弹窗的调用、自定义插槽内容、可配置参数及事件回调。

  methods:{
        openDialog(){
            this.$Modal({
                // 弹窗内嵌套组件
                component: xxx,
                // 嵌套组件传递属性
                componentProps: {

                },
                // 弹窗属性设置
                props: {

                },
                // 事件回调
                methods: {
                    
                }
            });
        }
 }
复制代码

写在前面

在实际开发场景用应用element-ui居多。所以插件以el-dialog为例。理论上是可以拓展为任意ui组件库相关弹窗业务使用的,这里大家可以举一反三一下。

本文权当技术分享,成果分享,如有需要转发使用,请带上原链接。thank you ~
如果有更好的方式欢迎交流~
最后,就是写文档的初衷其实是很单纯的,就是为了你们的点赞``点赞``点赞

动动你你们的小手 start ~

日常开发现状。

  • 冗余代码

多个弹窗需要定义需要定义多个属性。并且在业务使用过程中设置这些属性的true或者false

  • 可复用性差

页面多了很多非业务逻辑控制代码(可读性变差)。比如弹框一里面打开弹框二,控制代码却要写到弹框一的父页面。

  • 可维护性差

无关代码在业务过程中穿插,解藕性差。维护也相对吃力。需要查找你控制弹窗显示与否的属性-》再看你渲染的弹窗内容…时间已经过去了几个世纪

组件化开发还是要尽可能的解藕。让使用方式随心所欲。业务代码更加独立。组件复用性所有提升才行。

日常coding中

当业务中弹窗需要多个的时候基本如下,巴拉巴拉coding...,当然可能有其他的封装方式使用。但是单单的去写一些属性去控制这些弹窗的可见与否以及怎么把参数往子组件中传递.再者就是子组件业务处理完成要回调等等。这些看似繁琐而又看起来必要的东西。就这样写着写着会不会,有点不太漂亮(其实就是懒,不想写那么多代码。简单一点就好,极简一下更好)

<template>
    // .........举例子-巴拉巴拉一些弹窗代码.........
    <el-dialog
      title="车辆基本信息"
      width="50%"
      append-to-body
      destroy-on-close
      :visible="carInfoVisible"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :before-close="closeCarInfo"
    >
      <info
        v-if="carInfoVisible"
        :info-id="infoId"
        :car-info-visible="carInfoVisible"
        @close="closeCarInfo"
      />
    </el-dialog>
    
    <el-dialog
      title="车辆作业信息"
      width="50%"
      append-to-body
      destroy-on-close
      :visible="carInfoWorkVisible"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :before-close="closeCarWork"
    >
      <work
        v-if="carInfoWorkVisible"
        :info-id="infoId"
        :car-info-visible="carInfoWorkVisible"
        @close="closeCarWork"
      />
    </el-dialog>
  // ........如果还有其他业务需要弹 继续重复++.........
</template>


export default {
    data(){
        return {
            closeCarInfo:false,
            carInfoWorkVisible:false,
            .......略.......
        }
    },
    methods:{
        .....略一些弹窗组件的事件回调...
        closeCarWork(){}
    }
}

复制代码

懒癌发作开始思考

这些千篇一律的东西能不能优化掉呢?如果是用js调用的弹窗的方式的话,应该可以省掉一些代码。然后如果弹窗里面的内容可以自己定义传递的那是不是可以好点了。

  • 不想仅仅控制弹窗显示与否以及传递参数。让data中的变量变得很庞大。
  • 每个弹窗页面的内容组件是可以任意的,方便引入的使用。参数的传递和事件的回调都是简单而且都是可以自己定义的。

感受启发,其他组件库js调用方式是这样的

  methods:{
        openDialog(){
            this.$Modal({
                title: 'Js',
                content: '这是使用Js调用的弹出框'
            });
        }
  }
复制代码

而我期待的是这样的。

  • 可以设置弹窗属性
  • 要能方便自定义弹窗嵌套组件
  • 要可以设置自定义弹窗嵌套组件的传递属性
  • 要可以设置自定义弹窗嵌套组件的事件回调
  methods:{
        openDialog(){
            this.$Modal({
                // 弹窗内嵌套组件
                component: xxx,
                // 嵌套组件传递属性
                componentProps: {

                },
                // 弹窗属性设置
                props: {

                },
                // 事件回调
                methods: {
                    
                }
            });
        }
 }
复制代码

实现思路

每次使用调用的时候就实例化一个el-dialog组件同时设置 el-dialog的默认slots。考虑到同级使用和嵌套使用。所以在组件实例化的时候,都去 createElement一个div。作为组件挂载的元素。并且把这个元素至于body中。所以代码应该是这个样子的。

import Vue from 'vue'
let instance

const Modal = function ({ component, methods, props, componentProps }) {
  const dom = document.createElement('div')
  document.body.appendChild(dom)
  instance = new Vue({
      el: dom,
  })
}
export default Modal
复制代码

那么怎么去渲染弹窗和定义弹窗嵌套组件的呢?

方案1 template 模板渲染的方式(gg)

这是一开始想到的方式。巴拉巴拉coding...如下

import Vue from 'vue'
import { Dialog } from 'element-ui'

let instance

const template =
    '<Dialog :title="title" v-model="showModal"  @close="close">' +
        '<Plugin v-on:close="close" :componentProps="componentProps"> </Plugin>' +
    '</Dialog>'

const Modal = function ({ component, methods, props, componentProps }) {
  const dom = document.createElement('div')
  document.body.appendChild(dom)
  instance = new Vue({
      el: dom,
      template,
      components: {
       Dialog: Dialog,
       Plugin: component
      },
      data() {
          return {
              title:'弹窗描述Title',
              showModal:true
              ...如果还有更多的参数就不够灵活了...
          }
      },
      methods: {
        close(){
            this.showModal = false
        }
      },
  })
  return instance
}
export default Modal
复制代码

一顿调试之后发现这个方案并不是很好。编译报警告,即时处理了警告。如果有多参数的传递,后续也是一个灾难。所以这个方案到这里我就end了。如果有可以完成验证通过的同学可以留言告知一下。

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
复制代码

方案2 render 函数渲染的方式

render由于本身用的比较少。用的比较简单,只是作为单一的标签渲染。加上书写的方式有点小难受,一直也被当忽略一般。组件方式渲染的方式想都没有想过。指导看到下面的代码. createElement第一个参数支持组件选项对象!!!!。那就是说可以渲染组件咯。biubiu coding... 等等,按照这种方式那么怎么决定弹窗的内容组件?我们都知道el-dialog的内容是通过slot插槽插进去的。那么render可以去设置插槽吗?答案是可以的。通过设置scopedSlots

点击了解更多关于render函数

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一个 HTML 标签名、组件选项对象,或者
  // resolve 了上述任何一种的一个 async 函数。必填项。
  'div',

  // {Object}
  // 一个与模板中 attribute 对应的数据对象。可选。
  {
    // (详情见下一节)
  },

  // {String | Array}
  // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
  // 也可以使用字符串来生成“文本虚拟节点”。可选。
  [
    '先写一些文字',
    createElement('h1', '一则头条'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

// 深入数据对象
{
 // 作用域插槽的格式为
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
  // 如果组件是其它组件的子组件,需为插槽指定名称
  slot: 'name-of-slot',
}
复制代码

查阅render相关文档之后。最后的本质就是通过render–》变成template书写的那样。

import Vue from 'vue'
import { Dialog } from 'element-ui'
let instance

const Modal = function ({ component, methods, props, componentProps }) {
  const dom = document.createElement('div')
  document.body.appendChild(dom)
  instance = new vue({
    el: dom,
    data () {
      return {
        showModal: true
      }
    },
    components: {
      Dialog: Dialog,
      Plugin: component
    },
    render (createElement) {
      return createElement(
        'Dialog', {
          scopedSlots: {
            default: ()=> createElement('Plugin')
          }
        })
    },
  })
  return instance
}

export default Modal

复制代码

最后调整一下传递的props和回调的绑定的methods.弹窗内部的组件。并不需要一直纯在。期待在弹窗visible:true渲染既可。render的时候可以根据数据响应式渲染。设置scopedSlots在显示的时候渲染。

import Vue from 'vue'
import { Dialog } from 'element-ui'

let instance

const Modal = function ({ component, methods, props, componentProps }) {
  if (Vue.prototype.$isServer) return
  const dom = document.createElement('div')
  document.body.appendChild(dom)
  instance = new Vue({
    el: dom,
    data () {
      return {
        showModal: true
      }
    },
    components: {
      Dialog: Dialog,
      Plugin: component
    },
    render (createElement) {
      const plugin = this.showModal ? () => createElement('Plugin', {
        props: {
          ...componentProps
        },
        on: {
          close: (e) => this.close(e),
          ...methods
        }
      }) : null
      return createElement(
        'Dialog', {
          props: {
            visible: this.showModal,
            ...props
          },
          on: {
            close: (e) => this.close(e)
          },
          scopedSlots: {
            default: plugin
          }
        })
    },
    methods: {
      close () {
        this.showModal = false
        document.body.removeChild(this.$el)
        this.$destroy()
      }
    }
  })
  return instance
}

export default Modal
复制代码

插件使用

main.js中导入并装载到prototype原型中既可。

  // main.js
  import modal from '@/plugins/modal'
  Vue.prototype.$modal = modal
  
  // 使用以js的方式使用弹窗、配置的方式完成参数传递、事件回调
   this.$modal({
    // 弹窗内嵌套组件传递属性
    componentProps: {
      id: '7676043D-F0DA-80DA-1CBD-BDD7068B3A77'
    },
    // 弹窗内嵌套组件
    // component: Table, //同步
    component: (resolve) => require(['./cube-table-list.vue'], resolve), // 异步
    // 弹窗属性设置
    props: {
      title: '基本信息弹窗'
      // width: '520px',
      // fullscreen: false
    },
    // 事件回调
    methods: {
      refresh (e) {
        console.log(e, '--x-x-x-')
      }
    }
  })
复制代码

弹窗以js的方式调用。

以js的方式调用组件。以配置的方式决定弹窗内容。就可以省略掉这些一些玉女无瓜和业务无关属性、方法。弹窗是否关闭等不再是我们代码关心的。需要就调用渲染,关闭则销毁。支持无限极嵌套、多个同级使用。让开发更专注于业务的开发。

// 玉女无瓜的代码
<template>
    // .........举例子-巴拉巴拉一些弹窗代码.........
    <el-dialog
      title="车辆基本信息"
      width="50%"
      append-to-body
      destroy-on-close
      :visible="carInfoVisible"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :before-close="closeCarInfo"
    >
      <info
        v-if="carInfoVisible"
        :info-id="infoId"
        :car-info-visible="carInfoVisible"
        @close="closeCarInfo"
      />
    </el-dialog>
  // ........n++弹窗.........
</template>

复制代码

使用对应映射关系。

image.png

在最后

本代码插件是本人原创设计,因为在此之前也尝试寻找类似的解决方案。都没有找到合适的。然后就有了你们所看到的这个文章。当然如有雷同,权当没看见~。

本文权当技术分享,成果分享,如有需要转发使用,请带上原链接。thank you ~
如果有更好的方式欢迎交流~
最后,就是写文档的初衷其实是很单纯的,就是为了你们的点赞``点赞``点赞

动动你你们的小手 start ~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享