前言
平常我们写页面时基本上都要用到组件库,那你知道它们是如何封装实现的吗。继上次我们就试着了解下ElementUI的通用组件如何打造的,具体见 如何用VUE3打造通用组件 - 掘金 (juejin.cn)。接下来我们仿照着vue3的一个Element Plus框架中的Notification组件------实现比较基础的弹出框组件,打造出VUE公用弹出框组件!
先看效果图
准备
- 提前配置好环境
- 创建好目录
- 下载
Element Plus
包(主要是为了方便可以用到相应的样式,这样就不用手写考虑组件的css样式,所以在html文件中写的类名是规定好的,改动的话就不能用组件库里的样式,或者自行写样式也行)
csharp
yarn add element-plus
- 在
main.js
中引入Element Plus
正文
大致思路
- 基于在点击按钮后弹出相应的信息框------主要调用一个弹出框函数,传入相应的参数即可实现
- 打造弹出组件:首先搭建基本框架 ,可以放标题、内容以及关闭按钮,然后把整个结构用
teleport
包裹住,放到body
标签下(毕竟弹出框最好在全局生效),以及控制好组件之间的数据传递- 实现控制弹出框的位置:水平、垂直、偏移的位置。
- 实现js动态渲染的效果,这样可以通过调用一个函数并传入相应的值实现(加载notification.vue文件并渲染到页面)
- 主要是将vue组件通过vue中的h函数 ,编译成 vnoode,再通过 vue 中的render函数将vnode 渲染成真实的DOM,再将真实DOM添加到body标签下
一、编写notification.vue
文件(实现弹出框主文件)
- 搭建基本框架
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结构外层的位置去,也就是可以指定将其渲染到某个位置。
- 编写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标签下。
- 编写可以直接调用的函数实现弹出框,并将其抛出。
javascript
import { isVNode,h,render } from 'vue' //isVNode判断是否为vue的DOM结构
import notificationComponent from './notification.vue'
export function Notification(options){
//加载notification.vue文件并渲染到页面
return createNotification(options) //封装成另一个函数实现
}
- 实现
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)
}
- 实现
createNotificationByOpts
函数
scss
function createNotificationByOpts(opts){
if(isVNode(opts.message)){//如果所传参数的message是虚拟节点的时
return createComponent(notificationComponent,opts,()=>opts.message)
}
return createComponent(notificationComponent,opts)
}
- 实现
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结构
}
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>
效果图:
最后
本篇文章就到此为止啦,由于本人经验水平有限,难免会有纰漏,对此欢迎指正 。如觉得本文对你有帮助的话,欢迎点赞收藏❤❤❤。要是您觉得有更好的方法,欢迎评论,提出建议!