vue进阶:弹窗类组件实现方式

本文探讨了如何在Vue.js中实现弹窗类组件,包括其特点、实现思路及两种具体实现方式:借助Vue构造函数的"借鸡生蛋"式和使用Vue.extends()方法。讲解了如何将组件挂载到body并添加移除方法,以及在实际项目中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

弹窗类组件的特点

弹窗类组件的特点就是他们在当前的vue实例之外独立存在,通常挂载于body。他们是通过纯JS创建的,不需要在任何组件中声明。常见的食用方式是这样:

this.$create(Notice, {
	title: '标题标题', 
	message: '提示信息'
}).show();

分析实现思路

根据上面的实用方式,我们要想实现在vue组件中全局使用this.$create()方法,就需要将该方法赋值到vue的原型对象上。假设我们已经实现了create方法:

Vue.prototype.$create = create

分析下create函数,它接受两个参数,一个是组件配置对象,命名为Component,一个是参数属性,我们将他命名为props。要想将该组件配置对象挂载到body上,就需要构建Component实例,这样才能生成真实的dom节点。顺着这个思路,拿到真实的dom元素后,将该元素append到body上。再给其添加个remove方法,在移除的时候删除节点销毁自身,就大功告成了。

实现方式

借鸡生蛋式——借助Vue构造函数

在vue项目的main.js中,我们要想在body上挂载app组件,是这样写的:

new Vue({
  render: h => h(App)
}).$mount('#app')

这里的h就是createElement,它可以返回一个vnode即虚拟dom。$mount方法则可以将render函数中返回的vnode转化为真实节点,并挂载到目标元素上。需要注意一点:
$mount()是覆盖性操作。

所以我们在实现create时,不能够直接使用$mount(body),将真实节点挂载到body上。但是可以不设置挂载目标,这样依然可以转换vnode为真实节点。

所以我们也可以借助这种方式:

import Vue from 'vue'
export default function create (Component, props) {
  // 1. 构建Component的实例
  const vm = new Vue({
    render: (h) => {
      return h(Component, {props})
    }
    // 不能直接挂载在body节点上,因为$mount()是覆盖性操作.
  }).$mount()
  
  // 2.将真实节点挂载到body上,vm.$el是实例的真实节点。
  document.body.appendChild(vm.$el)

  // 3.获取组件实例
  const comp = vm.$children[0]

  comp.remove = () => {
    document.body.remove(vm.$el)
    vm.$destroy()
  }

  return comp
}

这样我们就是实现了create方法。我们写个例子来验证它:

// Notice组件

<template>
    <div v-if="isShow" class="modal">
        <div class="title">{{ title }}</div>
        <div class="content">{{ content }}</div>
    </div>
</template>

<script>
export default {
  name: 'Notice',
  props: {
    title: {
      type: String,
      default: '警告'
    },
    content: {
      type: String,
      default: '我是内容我是内容'
    }
  },
  data () {
    return {
      timerId: null,
      isShow: false
    }
  },
  methods: {
    show () {
      this.isShow = true
      setTimeout(() => {
        this.hide()
      }, 1500)
    },
    hide () {
      this.isShow = false
    }
  }
}
</script>

<style scoped>
.modal{
    position: relative;
    top: 50px;
    left: 50%;
    margin-left: -250px;
    width: 500px;
    border: 1px solid #f7f7f7;
    border-radius: 6px;
    padding: 10px;
    background-color: papayawhip;
    animation: disappear 2s ease-in-out;
}
.title{
    text-align: left;
    font-weight: 500;
    font-size: 14px;
}
.content {
    margin-bottom: 10px;
    font-size: 12px;
}

@keyframes disappear {
    0% {
        top: 60px;
        opacity: 1;
    }
    30% {
        top: 10px;
        opacity: 1;
    }
    96% {
      opacity: 0.20;
    }
    100% {
        top: 10px;
        opacity: 0;
    }
}
</style>

// index页面

<template>
    <div>
      <button @click="showNotice">提示弹窗</button>
    </div>
</template>

<script>
import Notice from './Source/NoticeModal/Notice.vue'
export default {
  name: 'Source',
  methods: {
    showNotice () {
      const notice = this.$create(Notice, {
        title: '测试标题',
        content: '我是内容内容呀呀呀'
      })
      notice.show()
    }
  }
}
</script>
效果:

在这里插入图片描述
来对比看下dom的渲染:
在这里插入图片描述

Vue.extends()方法

Vue.extends()方法可以使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。extend 创建的是 Vue 构造器,而不是我们平时常写的组件实例。

export default function create (Component, props) {
  // 1. 构建Component的实例
  const Ctor = Vue.extend(Component)
  
  // 2. 将props传递给创建的实例并挂载
  const vm = new Ctor({propsData: props}).$mount()

  // 将真实节点挂载到body上
  document.body.appendChild(vm.$el)
  
  vm.remove = () => {
    document.body.remove(vm.$el)
    vm.$destroy()
  }
  
  return vm
}

至此,一个纯js调用的弹窗类组件就完成了。参考文档:vuejs.org

♡ ♡ ♡ ♡ ♡ ♡
关注我,每日都有新收获!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值