如何打造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>

效果图:

最后

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

相关推荐
M_emory_26 分钟前
解决 git clone 出现:Failed to connect to 127.0.0.1 port 1080: Connection refused 错误
前端·vue.js·git
Ciito29 分钟前
vue项目使用eslint+prettier管理项目格式化
前端·javascript·vue.js
成都被卷死的程序员1 小时前
响应式网页设计--html
前端·html
mon_star°1 小时前
将答题成绩排行榜数据通过前端生成excel的方式实现导出下载功能
前端·excel
Zrf21913184551 小时前
前端笔试中oj算法题的解法模版
前端·readline·oj算法
文军的烹饪实验室3 小时前
ValueError: Circular reference detected
开发语言·前端·javascript
Martin -Tang4 小时前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发4 小时前
解锁微前端的优秀库
前端
王解5 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
我不当帕鲁谁当帕鲁5 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis