web技术分享| 基于vue3实现自己的组件库,第一章:Message组件
大家好今天将开始新的系列基于vue3实现自己的组件库
,本文默认你会安装和创建vue3项目,如果不会请参考vue官网,废话不多说开始实现本章的目标Message
组件;
创建组件库工程目录
vair是组件库的名字(名字大家随意)
安装项目依赖
npm install less -D
npm install less-loader -D
template
<template>
<div class='v-message'>
<div v-for='(config, index) in messageList' :key='config.id'
:ref= 'el => { if (el) contentList[index] = el}'
:class='["message-item", config.customClass, config.type, { center: config.center }]'>
<i :class='[config.iconClass, "icon"]'></i>
<p class='content'>{{ config.message }}</p>
<i class='close iconfont icon-guanbi1' @click='close(config)' v-if='config.showClose'></i>
</div>
</div>
</template>
script
import { ref, defineComponent } from 'vue';
export default defineComponent({
name: 'message',
setup () {
// 消息列表
const messageList = ref([]);
// ref列表
const contentList = ref([]);
const message = (options) => {
computedConfig(options);
}
const success = (options) => {
computedConfig(options, 'success');
}
const warning = (options) => {
computedConfig(options, 'warning');
}
const error = (options) => {
computedConfig(options, 'error');
}
const computedConfig = (options, type) => {
var option = options || {};
type && (option.type = type);
const config = {
type: option.type || 'prompt', // 没传消息类型就是默认消息
message: option.message || '',
iconClass: option.iconClass || computedIconClass(type || 'prompt'),
customClass: option.customClass,
duration: option.duration >= 0? option.duration : 3000,
showClose: option.showClose,
center: option.center,
onClose: option.onClose,
id: Math.floor(new Date())
};
messageList.value.push(config);
// 如果延时不等于0,就要设置消失时间
if (config.duration !== 0) {
setTimeout(() => {
contentList.value[0].className += ' messageHide';
setTimeout(() => {
messageList.value.splice(0, 1);
}, 200);
}, config.duration + messageList.value.length * 100);
}
};
const computedIconClass = (type) => {
switch (type) {
case 'prompt':
return 'iconfont icon-tishi';
case 'success':
return 'iconfont icon-success';
case 'warning':
return 'iconfont icon-jinggao--';
case 'error':
return 'iconfont icon-cuowu';
}
};
const close = (config) => {
const index = messageList.value.findIndex(item => item.id === config.id);
if (index !== -1) {
contentList.value[index].className += ' messageHide';
setTimeout(() => {
messageList.value.splice(index, 1);
config.onClose && config.onClose(config);
}, 200);
}
}
return {
messageList,
contentList,
close,
message,
success,
warning,
error
}
}
});
css
<style lang='less' scoped>
@import url('../../assets/css/animation.css');
.v-message {
position: fixed;
z-index: 99999;
top: 50px;
left: 0;
right: 0;
margin: auto;
width: 380px;
.message-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
box-sizing: border-box;
border-radius: 6px;
padding: 14px;
overflow: hidden;
border: 1px solid transparent;
animation: messageShow .5s;
animation-fill-mode: forwards;
.content {
font-size: 12px;
line-height: 20px;
flex: 1;
}
.close {
cursor: pointer;
&:hover {
color: #6b6b6b;
}
}
i {
font-size: 18px;
}
.icon {
margin-right: 14px;
}
}
.center {
justify-content: center;
.content {
flex: 0 1 auto;
}
}
.messageHide {
animation: messageHide .2s linear;
animation-fill-mode: forwards;
}
.prompt {
border: 1px solid #ebeef5;
background-color: #edf2fc;
.content, i {
color: #909399;
}
}
.success {
background-color: #f0f9eb;
border-color: #e1f3d8;
.content, i {
color: #67C23A;
}
}
.warning {
background-color: #fdf6ec;
border-color: #faecd8;
.content, i {
color: #E6A23C;
}
}
.error {
background-color: #fef0f0;
border-color: #fde2e2;
.content, i {
color: #F56C6C;
}
}
}
</style>
animation.css
这个文件的功能就是定义组件所需的动画
@keyframes messageShow {
0% {
transform: translateY(-50px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
@keyframes messageHide {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-50px);
opacity: 0;
}
}
编写message.js
import Message from "./Message.vue";
import { createApp } from "vue";
const createMessage = function () {
const div = document.createElement("div");
div.id = "v-message";
document.body.appendChild(div);
return createApp(Message).mount("#v-message");
};
export default createMessage();
编写vair出口 index.js
后续这个文件会引入很多组件
// Message 消息提示
import Message from './components/Message/message.js';
const Vair = function(Vue) {
// Message 消息提示
Vue.config.globalProperties.$message = Message;
}
export default Vair;
使用组件库
在main.js中引入
import { createApp } from 'vue';
import App from './App.vue';
import Vair from './libs/vair/index.js';
const app = createApp(App);
app.use(Vair).mount('#app');
App.vue中调用
<template>
<div class='message'>
<p class='button' @click='addMsg("success")'>成功</p>
<p class='button' @click='addMsg("warning")'>警告</p>
<p class='button' @click='addMsg("error")'>错误</p>
<p class='button' @click='addMsg("close")'>可关闭</p>
<p class='button' @click='addMsg("center")'>文字居中</p>
</div>
</template>
<script>
import { getCurrentInstance, defineComponent } from 'vue';
export default defineComponent({
setup () {
const app = getCurrentInstance();
const addMsg = (type) => {
if (type === 'success') {
app.ctx.$message.success({
message: '这是一条成功消息'
});
}
if (type === 'warning') {
app.ctx.$message.warning({
message: '这是一条警告消息'
});
}
if (type === 'error') {
app.ctx.$message.error({
message: '这是一条错误消息'
});
}
if (type === 'close') {
app.ctx.$message.success({
message: '这是一条可关闭的消息',
showClose: true,
onClose: (config) => {
console.log(config)
},
});
}
if (type === 'center') {
app.ctx.$message.success({
message: '这是一条居中的消息',
center: true
});
}
};
return {
addMsg
}
}
})
</script>
<style lang='less' scoped>
.message {
width: 500px;
display: flex;
justify-content: space-between;
}
.button {
width: 76px;
line-height: 34px;
border-radius: 4px;
color: #fff;
background-color: #1890FF;
text-align: center;
cursor: pointer;
font-size: 12px;
}
</style>
效果展示