2019-03-28

弹出类组件的设计思路

引用
基于Vue构造器创建Form组件的通用解决方案

使用 vue-cli(3.5)创建一个项目

.
├── node_modules
├── public
├── package.json
├── package-lock.json
└── src
    ├── App.vue
    ├── main.js
    └── components
       ├── Modal.vue
       ├── popup-win.js
       └── transform.js

transform.js 创建一个传入组件的实例

import Vue from "vue";

const inlineListen = ({
  method,
  options,
  instance
}) => {
  let listener = `on${method}`
  // 将事件绑定到instance上
  instance[listener] = options[method]
  instance.$on(method, function (data) {
    console.log("触发了事件 "+method)
    this[listener](data)
  })
}
// 传入一个组件,然后使用 propsData 创建一个组件对象,将回调函数注册到组件上
export default (component) => {
  const _constructor = Vue.extend(component)
  return function (options = {}) {
    const {
      propsData,
      done,
      cancel
    } = options

    let instance = new _constructor({
      propsData
    }).$mount(document.createElement('div'));
    done && inlineListen({
      method:'done',
      options,
      instance
    })
    cancel && inlineListen({
      method:'cancel',
      options,
      instance
    })
    return instance;
  }
}

popup-win.js 包含所有弹出类组件的生命周期函数,挂载到body、销毁

export default {
  data(){
    return {
      isVisible:true
    }
  },
  watch:{
    isVisible(newValue){
      if(!newValue){
        this.destoryElement()
      }
    }
  },
  mounted(){
    document.body.appendChild(this.$el)
  },
  destroyed(){
    console.log("触发了 destoryed");
    
    this.$el.parentNode.removeChild(this.$el)
  },
  methods:{
    destoryElement(){
      console.log("destoryElement")
      this.$destroy()
    },
    close(){
      this.isVisible = false
    }
  }
}

Modal.vue 通过 mixins扩展了 popup-win 里的数据和生命周期函数

<template>
  <!-- 此处若使用  v-if="isVisible" 每次销毁窗口后会留下一个 注释行 -->
  <div class="modal">
    <button @click="handleClick({ type: 'confirm' })">确定</button>
    <button @click="handleClick({ type: 'close' })">取消</button>
  </div>
</template>

<script>
import popupWin from "./popup-win.js";
export default {
  name:'modal',
  mixins:[popupWin],
  props:['name'],
  methods:{
    handleClick({type}){
      const handler ={
        close:()=>{
          this.$emit('cancel',name)
          this.close()
        },
        confirm:()=>{
          const {name} =this;
          console.log(name)
          this.$emit('done',name)
        }
      }
      handler[type] && handler[type]()
    },
  }
}
</script>

<style>
  .modal {
    height: 150px;
    width: 300px;
    position: fixed;
    z-index: 999;
    border: 1px solid black;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
  }
</style>

app.vue

<template>
  <div id="app">
    <button @click="handleClick">Modal</button>
  </div>
</template>

<script>
import Modal from './components/Modal.vue'
import transform from './components/transform.js''

export default {
  name: 'app',
  methods:{
    modal:transform(Modal),
    handleClick(){
      console.log('modal start')
      this.modal({
        propsData:{name:'是否删除'},
        done(name){
          console.log("执行了 done");
          alert(`确认`+name)
          this.close()
        },
        cancel(){
          console.log("执行了 cancel");
          this.close()
        }
      })
    }
  }
}
</script>

这种方法,可以直接调用函数来弹出窗口,通过提取公共逻辑,代码做到简洁,清晰。