如何打造VUE公用弹出框组件【简易版】

前言

平常我们写页面时基本上都要用到组件库,那你知道它们是如何封装实现的吗。继上次我们就试着了解下ElementUI的通用组件如何打造的,具体见 如何用VUE3打造通用组件 - 掘金 (juejin.cn)。接下来我们仿照着vue3的一个Element Plus框架中的Notification组件------实现比较基础的弹出框组件,打造出VUE公用弹出框组件

先看效果图

准备

  1. 提前配置好环境
  2. 创建好目录
  1. 下载Element Plus包(主要是为了方便可以用到相应的样式,这样就不用手写考虑组件的css样式,所以在html文件中写的类名是规定好的,改动的话就不能用组件库里的样式,或者自行写样式也行)
csharp 复制代码
yarn add element-plus
  1. main.js中引入Element Plus

正文

大致思路

  1. 基于在点击按钮后弹出相应的信息框------主要调用一个弹出框函数,传入相应的参数即可实现
  2. 打造弹出组件:首先搭建基本框架 ,可以放标题、内容以及关闭按钮,然后把整个结构用teleport包裹住,放到body标签下(毕竟弹出框最好在全局生效),以及控制好组件之间的数据传递
  3. 实现控制弹出框的位置:水平、垂直、偏移的位置。
  4. 实现js动态渲染的效果,这样可以通过调用一个函数并传入相应的值实现(加载notification.vue文件并渲染到页面)
  5. 主要是将vue组件通过vue中的h函数 ,编译成 vnoode,再通过 vue 中的render函数将vnode 渲染成真实的DOM,再将真实DOM添加到body标签下

一、编写notification.vue文件(实现弹出框主文件)

  1. 搭建基本框架
xml 复制代码
<template>
  <teleport to="body">
      //动态绑定位置的样式、不同类型弹出框的类名
    <div class="el-notification" :style="positionStyle" :class="typeClass">
    <div class="el-notification__group">
      <h2 class="el-notification__title">{{title}}</h2>
      <div class="el-notification__content">
        <p>{{message}}</p>
      </div>
    <button class="el-notification__close-button" @click="onCloseHandler">X</button>
    </div>
  </div>
  </teleport>
</template>

<style lang="css">
.el-notification__close-button{
  position:absolute;
  top:8px;
  right:15px;
  cursor:pointer;
  font-size:12px;
}
</style>

tips:

  • {{title}}{{message}}是从props.js文件中接收过来的值
  • teleport:由于要打造成公用的弹出框组件,那么组件应该在全局生效,应该出现在body标签下,所以这时候用到teleport------它是一个内置组件,可以将一个组件内部的一部分模块传送到该组件的DOM结构外层的位置去,也就是可以指定将其渲染到某个位置。
  1. 编写js
xml 复制代码
<script> 
  import { ref,computed } from 'vue'
  import { notificationProps } from './props.js'
  export default {
    props:notificationProps,
    setup(props){//props代表的是子组件接收到父组件传的参数
      const visible=ref(true) //组件自己控制是否展示
      const vertialOffsetVal=ref(props.offset)//垂直偏移位置初始值

      const typeClass=computed(()=>{//动态添加不同类型的弹出框的类名,为了后面可以给不同类型的弹出框展示不同的效果
        return props.type?`el-icon-${props.type}`:''
      })

      const horizationalClass=computed(()=>{//水平方向上的位置
        return props.position.endsWith('right')?'right':'left'
      })

      const verticalProperty=computed(()=>{//竖直方向上的位置
        return props.position.startsWith('top')?'top':'bottom'
      })

      const positionStyle=computed(()=>{ //偏移的距离
        return {
          [verticalProperty.value]:`${vertialOffsetVal.value}px`,
          [horizationalClass.value]:'0px'
        }
      })
      
      const onCloseHandler=()=>{//关闭弹出框
        let p=document.querySelector('.el-notification')
        document.body.removeChild(p) //从body中移除指定的弹出框dom结构
      }

      return {
        typeClass,
        positionStyle,
        verticalProperty,
        horizationalClass,
        vertialOffsetVal,
        visible,
        onCloseHandler
      }
    }
  }
</script>

二、编写props.js文件(实现父组件传递的参数情况)

notification.vue中将子组件接收到父组件传递的参数有哪些以及其类型默认值抽离出来,写在此文件中(在这里就写了其中一些参数类型)。

go 复制代码
export const notificationProps = {
  title: { type: String, default: '' },
  message: { type: String, default: '' },
  position: { type: String, default: 'top-right' },//默认位置是在右上角处
  offset: { type: Number, default: 0 },
  type: { type: String, default: '' }
}

至此基本上就可以实现以组件的形式展示弹出框的效果。但是由于Element plus中使用时是调用一个函数,并且传入参数才实现,接下来我们考虑把notification.vue转换为js文件,并以函数的形式抛出。

三、编写index.js文件(将notification.vue转换为js文件,编写成函数形式)

  • 思路 :将vue组件通过vue中的 h函数 ,编译成 vnoode,再通过 vue 中的 render函数将vnode 渲染成真实的DOM,再将真实DOM添加到body标签下。
  1. 编写可以直接调用的函数实现弹出框,并将其抛出。
javascript 复制代码
import { isVNode,h,render } from 'vue' //isVNode判断是否为vue的DOM结构
import notificationComponent from './notification.vue'

export function Notification(options){
  //加载notification.vue文件并渲染到页面
  return createNotification(options) //封装成另一个函数实现
}
  1. 实现createNotification函数
scss 复制代码
function createNotification(options){//用js来创建组件
  const instance=createNotificationByOpts(options)//将参数的值进行融合
  addToBody(instance)//添加到body标签下
  return instance.proxy //vue编译的时候就存在proxy属性
}

function addToBody(instance) {
  document.body.appendChild(instance.vnode.el)
}
  1. 实现createNotificationByOpts函数
scss 复制代码
function createNotificationByOpts(opts){
  if(isVNode(opts.message)){//如果所传参数的message是虚拟节点的时
    return createComponent(notificationComponent,opts,()=>opts.message)
  }
  return createComponent(notificationComponent,opts)
}
  1. 实现createComponent函数(核心)
javascript 复制代码
function createComponent(Component,props,children){//将vue组件变成js组件
  const vnode = h(Component, {...props, ref: 'el_component'}, children)// 编译 将vue组件转换为虚拟DOM结构
  const container = document.createElement('div')
  vnode[Symbol('el_component_container')] = container //给vnode创建一个独一无二的key,并赋值为空的div容器
  render(vnode, container) //渲染 通过render渲染为真实的DOM
  return vnode.component //返回真实的DOM结构
}
  1. index.js全部代码
javascript 复制代码
import { isVNode,h,render } from 'vue' //判断是否为vue的DOM结构
import notificationComponent from './notification.vue'

export function Notification(options){
  //加载notification.vue文件并渲染到页面
  return createNotification(options)
}

function createNotification(options){//用js来创建组件
  const instance=createNotificationByOpts(options)
  addToBody(instance)
  return instance.proxy //vue编译的时候就存在proxy属性
}

function createNotificationByOpts(opts){
  if(isVNode(opts.message)){//如果所传参数的message是虚拟节点的时
    return createComponent(notificationComponent,opts,()=>opts.message)
  }
  return createComponent(notificationComponent,opts)
}

function createComponent(Component,props,children){//将vue组件变成js组件
  const vnode = h(Component, {...props, ref: 'el_component'}, children)// 编译 将vue组件转换为虚拟DOM结构
  const container = document.createElement('div')
  vnode[Symbol('el_component_container')] = container //给vnode创建一个独一无二的key,并赋值为空的div容器
  render(vnode, container) //渲染 通过render渲染为真实的DOM
  return vnode.component //返回真实的DOM结构
}
function addToBody(instance) {
  document.body.appendChild(instance.vnode.el)
}

至此,一个以函数方式引入组件的弹出框组件就打造好了,现在我们来使用试下吧!

四、实践

App.vue文件下编写以下代码:

xml 复制代码
<template>
  <div>
    <button @click="openNotify">点击弹出Notification</button>
  </div>
</template>

<script setup>
import {Notification} from './components/Notification/index.js'

const openNotify=()=>{
      Notification({
        title:'标题',
        message:'渲染信息',
        position:'bottom-right',
        offset:50,
      })
  }
</script>

<style lang="css">
body{
  background:pink;
}
</style>

效果图:

最后

本篇文章就到此为止啦,由于本人经验水平有限,难免会有纰漏,对此欢迎指正 。如觉得本文对你有帮助的话,欢迎点赞收藏❤❤❤。要是您觉得有更好的方法,欢迎评论,提出建议!

相关推荐
2分钟速写快排27 分钟前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60611 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角2 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!2 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰2 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi4 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen5 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马5 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒6 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端
乘风gg6 小时前
DeepSeek V4.1 Flash 来了,明天中午 Flash 降价 60%
前端·ai编程·claude