vue进阶知识-03-使用render、extend两种方式手写弹框

844 阅读3分钟

弹窗类组件设计思路: 它们在当前vue实例之外独立存在,通过JS动态创建,不需要在任何组件中声明,然后挂载在body上。

方式1: render()函数

实现创造函数

新建一个创造函数来实现。

src下新建一个utils文件夹,表示工具类文件,用来存放工具函数。再文件夹下新建一个swCreateBox.js文件,用来实现创建弹窗组件。

import Vue from 'vue';

// 函数接收两个参数:
// 参数1: 是组件配置对象
// 参数2: 是传递进来的参数(propsData)
function swCreateBox(Component, props) {}

export default swCreateBox;

第一步

借用Vue构造函数动态生成组件实例(得到虚拟dom):

const vm = new Vue({
    render(h) { //  render函数将传⼊组件配置对象转换为虚拟dom
        // h是createElement别名,返回一个虚拟dom(VNode)
        return h(Component, { props });
    }
});

第二步

执⾏挂载函数来得到真实dom:

vm.$mount();// 但未指定挂载⽬标,表示只执⾏初始化⼯作,就会创建真实dom,但是不会追加操作

第三步

通过$el属性获取真实dom元素再添加到body:

document.body.appendChild(vm.$el);

第四步

得到组件实例:

 const mycomponent = vm.$children[0];

第五步

因为弹框需要被关闭,所以给弹框添加销毁⽅法:

mycomponent.remove = () => {
    document.body.removeChild(vm.$el);
    vm.$destroy();
};

最后

返回这个组件实例

return mycomponent;

完整代码

import Vue from 'vue';

// 创建一个函数,动态生成组件实例挂载到body上
// Component:是组件配置对象
function swCreateBox(Component, props) {
    // 创建⼀个Vue新实例:借用Vue构造函数来动态生成这个组件实例
    const vm = new Vue({
        render(h) { //  render函数将传⼊组件配置对象转换为虚拟dom
            // h是createElement别名,返回一个虚拟dom(VNode)
            return h(Component, { props });
        }
    });
    // 执⾏挂载函数,但未指定挂载⽬标,表示只执⾏初始化⼯作,可以得到真实dom
    vm.$mount();
    // 通过$el属性获取真实dom元素再添加到body
    document.body.appendChild(vm.$el);
    // 得到组件实例
    const mycomponent = vm.$children[0];
    //  再给组件实例添加销毁⽅法
    mycomponent.remove = () => {
        document.body.removeChild(vm.$el);
        vm.$destroy();
    };
    return mycomponent;
}

export default swCreateBox;

将方法添加到vue原型

因为可能多处使用,所以我们将创造函数添加到vue原型上去:

main.js中:

import swCreateBox from '@/utils/swCreateBox.js'

Vue.prototype.$swCreateBox = swCreateBox;

新建通知组件

components下新建一个SwMsgBox.vue文件,组件里有一个状态isShow来判断是否显示,接收title(标题)、content(内容)、duration(延迟时间)三个prop属性:

<template>
  <div v-if="isShow">
    <!-- 标题 -->
    <h3>{{ title }}</h3>
    <!-- 内容 -->
    <p>{{ content }}</p>
  </div>
</template>

<script>
export default {
  props: {
    title: {
      type: String,
      default: "",
    },
    content: {
      type: String,
      default: "",
    },
    duration: {
      // 延迟时间:多久之后关闭弹框(毫秒)
      type: Number,
      default: 1000,
    },
  },
  data() {
    return {
      isShow: false, // 是否显示自己
    };
  },
  methods: {
    show() {
      this.isShow = true;
      setTimeout(this.hide, this.duration); // 指定时间之后执行hide方法
    },
    hide() {
      this.isShow = false; // 隐藏自己
      this.remove(); // 从body中卸载销毁自己 ->  写在创造方法中的
    },
  },
};
</script>

<style scoped>
div {
  position: fixed;
  min-width: 200px;
  top: 16px;
  left: 40%;
  text-align: center;
  pointer-events: none;
  background-color: greenyellow;
}
</style>

使用

先引入:

import SwMsgbox from "@/components/messageBox/SwMsgBox";

在需要使用的地方使用:

click() {
    this.$swCreateBox(SwMsgbox, {
    title: "pyy",
    content: "成功!",
    duration: 3000,
    }).show();
},

方式2: extend()函数

实现创造函数

utils文件夹下新建一个swExtendBox.js文件

import Vue from 'vue';

function swExtendBox(Component, props) {}

export default swExtendBox;

第一步

利用vue的extend()方法来得到组件的构造函数:

const MyConstructor = Vue.extend(Component); // 构造函数函数名首字母应该大写

因为参数2是propsData,所以不能在这儿传递,那么怎么传递参数呢?

第二步

通过构造函数创建组件实例,并传递prop参数:

const mycomponent = new MyConstructor({propsData: props});

第三步

执⾏挂载函数来得到真实dom:

mycomponent.$mount();

第四步

通过组件的$el属性获取真实dom元素再添加到body:

document.body.appendChild(mycomponent.$el);

第五步

因为弹框需要被关闭,所以给弹框添加销毁⽅法:

 mycomponent.remove = () => {
     document.body.removeChild(mycomponent.$el);
     mycomponent.$destroy();
 };

最后

返回这个组件实例

return mycomponent;

完整代码

import Vue from 'vue';

function swExtendBox(Component, props) {
    // 利用vue的extend()方法来得到组件的构造函数
    const MyConstructor = Vue.extend(Component);
    // 通过构造函数创建组件实例,并传递prop参数
    const mycomponent = new MyConstructor({ propsData: props });
    // 执⾏挂载函数来得到真实dom
    mycomponent.$mount();
    // 通过组件的$el属性获取真实dom元素再添加到body
    document.body.appendChild(mycomponent.$el);
    // 给弹框添加销毁⽅法
    mycomponent.remove = () => {
        document.body.removeChild(mycomponent.$el);
        mycomponent.$destroy();
    };
    return mycomponent;
}

export default swExtendBox;

将方法添加到vue原型

main.js中:

import swExtendBox from '@/utils/swExtendBox.js'

Vue.prototype.$swExtendBox = swExtendBox;

使用

参照方式1

封装成插件使用

在swCreateBox.js文件创造函数当中:

// 使⽤插件进⼀步封装便于使⽤
export default {
    install(Vue) {
        Vue.prototype.$swCreateBox = function (options) {
            return swCreateBox(SwMsgbox, options);
        }
    }
}

在swExtendBox.js文件创造函数当中:

// 使⽤插件进⼀步封装便于使⽤
export default {
    install(Vue) {
        Vue.prototype.$swExtendBox = function (options) {
            return swExtendBox(SwMsgbox, options);
        }
    }
}

main.js中:

import swCreateBox from '@/utils/swCreateBox.js'
import swExtendBox from '@/utils/swExtendBox.js'

Vue.use(swCreateBox);
Vue.use(swExtendBox);

在使用的vue文件中:

// 不需要再引入,直接使用
// this.$swCreateBox({
//   title: "pyy",
//   content: "成功!",
//   duration: 3000,
// }).show();
this.$swExtendBox({
  title: "wyz",
  content: "成功!",
  duration: 3000,
}).show();

代码地址

代码github地址