Vue 弹框组件二次封装

1,146 阅读2分钟

What

这篇文章主要写 vue 弹框组件二次封装,即我们在原有组件上再包装一层。

Why

由于弹框组件涉及的内容比较多,可以算是一个比较复杂的组件了。它包括弹框主体、内容区、以及按钮操作。如果我们在同一个页面里面引用的话,会有非常大的代码量,即使内容区独立出来仍然不少,而且独立出来又涉及到组件之间通信的麻烦事。这篇文章就是解决这个问题,使得弹框组件简洁易用。

Where

适用于所有提示类弹框及业务类弹框,不适用消息框。

How

  • 在根目录或者 src 目录新建 Alert.js,内容如下:
    // Alert.js
    import Vue from 'vue'
    import { Dialog, Button } from 'element-ui'
    export default function create(Component, props, options) {
      const vm = new Vue({
        data() {
          return {
            visible: true
          }
        },
        render() {
          const onHandleClose = (e, done) => {
            if (done) done()
            options.onClose()
            if (options.destroyInstance) {
              comp.remove()
            } else {
              comp.hide()
            }
          }
          const onHandleMethod = (e, item) => {
            if (props && props.ref) {
              item.callback(this.$refs[props.ref])
            } else {
              item.callback()
            }
            if (item.close) {
              if (options.destroyInstance) {
                comp.remove()
              } else {
                comp.hide()
              }
            }
          }
          return (
            <Dialog
              title={options.title}
              visible={this.visible}
              before-close={(e, done) => onHandleClose(e, done)}
            >
              {
                typeof Component === 'object'
                  ? props.ref
                    ? <Component ref={props.ref} props={props} />
                    : <Component props={props} />
                  : Component
              }
              {
                options.showButtons
                  ? <span slot="footer" class="dialog-footer">
                    {
                      options.buttons.map((item, index) => (
                        index === options.buttons.length - 1
                          ? <Button key={index} onClick={e => onHandleMethod(e, item)}>{item.title}</Button>
                          : <Button type="primary" key={index} onClick={e => onHandleMethod(e, item)}>{item.title}</Button>
                      ))
                    }
                  </span>
                  : null
              }
            </Dialog>
          )
        }
      }).$mount()
    
      document.body.appendChild(vm.$el)
    
      const comp = vm.$children[0]
      comp.show = () => {
        vm.$data.visible = true
      }
      comp.hide = () => {
        vm.$data.visible = false
      }
      comp.remove = () => {
        document.body.removeChild(vm.$el)
        comp.$destroy()
      }
    
      return comp
    }
    
  • Alert 组件挂到 Vue 原型上:
    // main.js
    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    import ElementUI from 'element-ui'
    import 'element-ui/lib/theme-chalk/index.css'
    
    import Alert from '../Alert.js'
    Vue.prototype.$alert = Alert
    
    Vue.config.productionTip = false
    Vue.use(ElementUI)
    
    new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount('#app')
    
  • HelloWorld 组件:
    // HelloWorld.vue
    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
        <p> For a guide and recipes on how to configure / customize this project</p>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      props: {
        msg: String
      }
    }
    </script>
    
  • 组件中使用:
    // App.vue
    <template>
      <div id="app">
        <div id="nav">
          <router-link to="/">Home</router-link> |
          <router-link to="/about">About</router-link>
        </div>
      </div>
    </template>
    <script>
    import HelloWorld from './components/HelloWorld'
    export default {
      mounted() {
        const vm = this.$alert(HelloWorld, {
          msg: 'HelloWorld', // 内容区组件属性传递
          ref: 'hello' // 要获取内容区组件内容该字段必传
        }, {
          title: '标题', // 弹框标题
          showButtons: true, // 是否显示操作按钮
          buttons: [ // 按钮配置:[标题,点击后是否关闭弹框, 点击后执行的事件]
            {
              title: '取消',
              close: true,
              callback: () => {
                alert('取消')
              }
            },
            {
              title: '确定',
              close: false,
              callback: (data) => { // data 是内容区组件实例
                console.log(data.msg)
                vm.hide() // destroyInstance=false 时设置 display=none
                alert('确定')
                setTimeout(() => {
                  vm.show() // destroyInstance=false 时设置 display=block
                  setTimeout(() => {
                    vm.remove() // 移除销毁 dom
                  }, 1000)
                }, 1000)
              }
            }
          ],
          destroyInstance: false // 关闭时弹框是否移除销毁 dom
        })
      }
    }
    </script>
    

So

经过这样封装,弹框组件使用起来是不是非常简单呢,希望对大家有用,感谢大家!