Vue实现Dialog封装

 更新时间:2021年07月11日 09:00:55   作者:TMaize  
在写业务的时候很常见的一个场景就是需要在不同的页面调用同一个表单,常用的交互就是把表单以弹窗的形式展示,本文主要介绍了Vue实现Dialog封装,感兴趣的可以了解一下

在写业务的时候很常见的一个场景就是需要在不同的页面调用同一个表单,常用的交互就是把表单以弹窗的形式展示,但是在每个页面又重复的引入表单组件有时候又很麻烦

解决方案有两个:

  1. 在根组件里面引入动态组件,在业务里面通过this.$root.openDialog(name, props)去控制动态组件的展示形式
  2. 封装成插件的形式去调用,比如this.$dialog('EditDialog.vue', props)

当然了,业务 Dialog 组件要有一套规范,props 接收一个 onOk、onCancel 回调,data 里面定义一个 visible 属性

<template>
  <el-dialog :title="title" :visible.sync="visible" append-to-body>
    <!-- 业务代码 -->
  </el-dialog>
</template>

<script>
export default {
  props: ['onOk', '其他业务需要的属性'],
  data() {
    return {
      visible: false
    }
  }
}
</script>

Vue2 写法

在 Vue2 里面我个人感觉写成插件是比较好用的,实现如下,使用混入做了一些操作,和业务进行解耦

有点不太好的地方是组件是动态插入的,Vue devtools 要刷新下才能看到组件

const mixin = {
  mounted() {
    document.body.appendChild(this.$el)
    this.visible = true
  },
  watch: {
    visible(value) {
      // 动画结束后销毁实例
      if (value === false) {
        setTimeout(() => {
          this.$destroy()
          if (this.$el && this.$el.parentNode) {
            this.$el.parentNode.removeChild(this.$el)
          }
        }, 400)
      }
    }
  }
}

export default {
  install(Vue, options) {
    Vue.prototype.$dialog = (name, props) => {
      // 相对于该插件的位置,静态编译期间会检查的
      import('../components/dialogs/' + name)
        .then(module => {
          const component = module.default
          const mixins = component.mixins || []
          mixins.push(mixin) // 实现自动打开,动态了混入生命周期函数和销毁操作
          component.mixins = mixins
          return Vue.extend(component)
        })
        .then(Dialog => {
          const dialog = new Dialog({
            propsData: props || {}
          })
          dialog.$mount()
        })
    }
  }
}

调用方式如下,注意 onOk 回调的 this 指向,使用箭头函数直接就避免了 😎

this.$dialog('GroupEdit.vue', {
  type: 'edit',
  group: {},
  onOk: () => {
    this.freshList()
  }
})

Vue3 插件版写法

很糟糕的是,由于 Vue3 的升级Vue.extend没有了,$mount也没有了,组件只能在应用里面去渲染

每个应用之间的数据是隔离的,所以插件什么的都要重新引入。同时如果要交互交互的话也比较麻烦,引入同一个 vuex 实例应该可以,但是没怎试

为了低耦合只能去新建一个应用去挂载渲染

import { createApp, defineComponent } from 'vue'
import ElementPlus from 'element-plus'

const mixin = {
  mounted() {
    document.body.appendChild(this.$el)
    this.visible = true
  },
  watch: {
    visible(value) {
      // 动画结束后销毁实例
      if (value === false) {
        setTimeout(() => {
          this.$.appContext.app.unmount()
        }, 400)
      }
    }
  }
}

export default {
  install(app) {
    app.config.globalProperties.$dialog = (name, props) => {
      import('../components/dialogs/' + name)
        .then(module => {
          const component = module.default
          let mixins = component.mixins || []
          mixins.push(mixin)
          component.mixins = mixins

          return defineComponent(component)
        })
        .then(Dialog => {
          const app = createApp(Dialog, props || {})
          app.use(ElementPlus)
          app.mount(document.createElement('div'))
        })
    }
  }
}

Vue3 动态组件写法

在 Vue3 里面,插件版的写法同样达到了要求,但是完全是一个新引应用了,如果在业务里访问this.$root,vuex,router还是有点麻烦的

所以 Vue3 里面还是动态组件的写法比较好

在根组件引入动态 component,定义一些控制变量

<template>
  <router-view></router-view>
  <component :is="currentDialog" v-bind="currentDialogProps" />
</template>

<script>
export default {
  data() {
    return {
      currentDialog: null,
      currentDialogProps: null
    }
  }
}
</script>

调用的的话this.$root.$dialog(),看起来太难看,其实还是可以手动模拟插件的效果的

const app = createApp(App)
const vm = app.mount('#app')

initDialog(app, vm)

function initDialog(app, vm) {
  const mixin = {
    mounted() {
      this.visible = true
    },
    watch: {
      visible(value) {
        // 动画结束后销毁实例
        if (value === false) {
          setTimeout(() => {
            this.$root.currentDialog = null
            this.$root.currentDialogProps = {}
          }, 400)
        }
      }
    }
  }

  app.config.globalProperties.$dialog = (name, props) => {
    import('./components/dialogs/' + name).then(module => {
      const component = module.default
      let mixins = component.mixins || []
      mixins.push(mixin)
      component.mixins = mixins
      // 不需要 defineComponent(component)
      vm.currentDialog = markRaw(component)
      vm.currentDialogProps = markRaw(props || {})
    })
  }
}

一些比较 hack 的写法

vue3 组件实例获取应用实例

vm.$.appContext.app == app

vue3 应用实例获取组件实例,注意_instance 仅在 dev 环境能访问到

app._instance.proxy == vm
app._instance.root.proxy == vm
app._instance.ctx.$root == vm

骚操作还是有的,但是最好不要用

const app = createApp(App)
const vm = app.mount('#app')

if (process.env.NODE_ENV === 'production') {
  app._instance = {
    proxy: vm,
    root: {
      proxy: vm
    },
    ctx: {
      $root: vm
    }
  }
}

到此这篇关于Vue实现Dialog封装的文章就介绍到这了,更多相关Vue Dialog封装内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue子页面控制父页面刷新问题

    vue子页面控制父页面刷新问题

    这篇文章主要介绍了vue子页面控制父页面刷新问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • vue实现将一维数组变换为三维数组

    vue实现将一维数组变换为三维数组

    这篇文章主要为大家详细介绍了vue如何实现将一维数组变换为三维数组,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-01-01
  • vue-ajax小封装实例

    vue-ajax小封装实例

    下面小编就为大家带来一篇vue-ajax小封装实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • vue项目Luckysheet的使用

    vue项目Luckysheet的使用

    这篇文章主要介绍了vue项目Luckysheet的使用,目前Luckysheet不支持使用npm安装包,所以只能使用CDN引入依赖,在vue项目的public/index.html文件里引入即可,本文通过示例代码给大家详细介绍,需要的朋友可以参考下
    2022-08-08
  • vue项目自适应屏幕分辨率实现步骤

    vue项目自适应屏幕分辨率实现步骤

    这篇文章主要给大家介绍了关于vue项目自适应屏幕分辨率实现的相关资料,作为前端人员,为了适配各种型号的电脑、手机,我们往往离不开屏幕分辨率的适配,需要的朋友可以参考下
    2023-09-09
  • 使用vue-cli导入Element UI组件的方法

    使用vue-cli导入Element UI组件的方法

    这篇文章给大家介绍了使用vue-cli导入Element UI组件的方法,非常不错,具有一定的参考借鉴价值,需要的朋友一起看看吧
    2018-05-05
  • vue自定义开关组件使用详解

    vue自定义开关组件使用详解

    这篇文章主要为大家详细介绍了vue自定义开关组件的使用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • Vue使用预渲染代替SSR的方法

    Vue使用预渲染代替SSR的方法

    这篇文章主要介绍了Vue使用预渲染代替SSR的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Map.vue基于百度地图组件重构笔记分享

    Map.vue基于百度地图组件重构笔记分享

    这篇文章主要为大家分享了Map.vue基于百度地图组件重构笔记,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • vue 不使用select实现下拉框功能(推荐)

    vue 不使用select实现下拉框功能(推荐)

    这篇文章主要介绍了vue 不使用select实现下拉框功能,在文章给大家提到了vue select 组件的使用与禁用,需要的朋友可以参考下
    2018-05-05

最新评论