【Vue】Element-Plus 源码学习笔记——实现一个基本的 ElMessage 组件

先贴下 Element-Plus ElMessage 源码的网址。我们需要实现的效果类似 ElMessage,即能够显示多个消息、上一个消息消失下面的消息会自动往上移动、进入移出动画、自定义消息和持续时间。其他选项这里不考虑。

大体思路
我们的 ElMessage 组件通过函数调用动态显示,无需事先在页面中放入组件,这样能更灵活也更方便。实现的基本思路是使用函数动态渲染组件到页面上。

createVNode 和 render
实际上 createVNodeh 函数的别名,这两个函数的功能是创建一个 VNode,这个 VNode 可以理解为是 DOM 的描述,当我们要渲染 VNode 到真正的页面时,就要用到 render 函数。我们可以封装一个 MessageBox.vue 的组件,然后通过 createVNode 创建对应组件的 VNode,用 render 把组件渲染到页面上。

message.vue

<template>
  <transition name="fade" @before-leave="onClose" @after-leave="onDestroy">
    <div :style="{ top: topOffset + 'px' }" class="message" v-if="visiable">
      {{ message }}
    </div>
  </transition>
</template>

<script>
export default {
  props: {
    topOffset: {
      type: Number,
      required: true,
    },
    message: {
      type: String,
      required: true,
    },
    duration: {
      type: Number,
      default: 3000,
    },
    id: {
      type: Number,
      required: true,
    },
    onDestroy: {
      type: Function,
      required: true,
    },
    onClose: {
      type: Function,
      required: true,
    },
  },
  data() {
    return {
      visiable: false,
    };
  },
  methods: {
    close() {
      this.visiable = false;
    },
  },
  mounted() {
    this.visiable = true;
    setTimeout(() => {
      this.close();
    }, this.duration);
  },
};
</script>

<style scoped>
.message {
  position: fixed;
  left: 50%;
  transform: translate(-50%, 10px);
  background-color: burlywood;
  padding: 0.5rem;
  width: 300px;
  z-index: 10001;
  transition: top 0.4s;
}

.fade-enter-active,
.fade-leave-active {
  transition: transform 0.4s, opacity 0.4s;
}

.fade-enter-from {
  transform: translate(-50%, 0);
  opacity: 0;
}


.fade-leave-to {
  transform: translate(-50%, -10px) !important;
  opacity: 0;
}
</style>

组件的基本样式这里不进行讲解,我们把重点放到 transition 动画上面。

v-enter-active、v-leave-active
这两个属性在整个动画期间都会生效,一般用来写动画的过渡属性 transition

v-enter-to、v-leave-to
在进入过渡触发前、离开过渡被触发后生效。

v-enter-from、v-leave-from
定义进入、离开过渡的初始状态。

在 message 进入时我们想显示一个 message 向下移动的动画,于是给 message 设置了默认的 translateY(10px)。而 v-enter-from 触发后,translateY(0) 生效,下一帧被移除,于是会执行 translateY(0px) -> translateY(10px) 的动画,达到效果。

显示元素 message.js

import {
  remove
} from '@vue/shared';
import {
  createVNode,
  render
} from 'vue'
import MessageBox from '../components/MessageBox.vue'

let seed = 0;
const instance = [];
const appendTo = document.body;

export default function (message, duration) {
  let topOffset = 20;
  const container = document.createElement('div');

  // 计算当前元素距离顶部的偏移量
  instance.forEach(vm => {
    topOffset += (vm.el.offsetHeight || 0) + 16;
  });

  // 保存 id;这行代码是必要的,因为当关闭定时器触发的时候,seed 的值为最后一次增加的值,不保存直接用 seed 会出错
  const id = seed;

  const vm = createVNode(MessageBox, {
    id,
    message,
    duration,
    topOffset,
    // 组件销毁时触发的回调
    onDestroy() {
      render(null, container);
      container.remove();
    },
    // 组件关闭时触发的回调
    // 这个回调用于显示组件的移出动画,和 onDestroy 不冲突
    onClose() {
      close(id)
    }
  })

  // 渲染组件到 container 上
  render(vm, container);
  // 添加 container 到 body
  appendTo.appendChild(container);
  // 保存组件实例,销毁时会用到
  instance.push(vm);

  seed++;
}

const close = (id) => {
  const idx = instance.findIndex(vm => vm.props.id === id);

  if (idx === -1) {
    return;
  }

  const vm = instance[idx];
  const removedHeight = vm.el.offsetHeight;
  instance.splice(idx, 1);

  for (let i = idx; i < instance.length; i++) {
    // 直接赋值组件的 top 为减去移除组件后的高度
    const pos = parseInt(instance[i].el.style['top']) - removedHeight - 16;
    // topOffset = topOffset - removedHeight - 16 同理
    instance[i].component.props.topOffset = pos;
  }
}

App.vue 测试显示组件

<script setup>
import message from "./hooks/useMessageBox";
import { ref } from "vue";

const count = ref(1);
</script>

<template>
  <button @click="message('lala' + count, 2000)">
    show message box
  </button>
</template>

<style></style>```
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Element Plus是一个基于Vue 3.0的UI组件库,是对Element UI的升级适配。它使用了TypeScript和Composition API进行重构,几乎重写了每一行Element UI的代码,以最适合Vue 3.0的方式进行开发。你可以通过直接在浏览器中导入Element Plus的HTML标签来使用,也可以在Vue项目中通过导入组件库和样式来使用。其中,你可以通过导入Element Plus的库和样式,以及注册所有的图标来使用全部组件和图标。另外,在Vue项目的main中,你也可以配置Element Plus。代码示例如下: ``` // 导入 element-plus import ElementPlus from "element-plus"; // 导入 element-plus 样式 import "element-plus/dist/index.css"; // 导入 element-plus 图标 import * as ElementPlusIconsVue from "@element-plus/icons-vue"; // 注册所有图标 for (const [key, component of Object.entries(ElementPlusIconsVue)) { app.component(key, component); } // 在main中配置Element-Plus import ElementPlus from 'element-plus'; import 'element-plus/theme-chalk/index.css'; createApp(App).use(store).use(router).use(ElementPlus).mount('#app') ``` 这样你就可以在Vue项目中使用Element Plus来开发UI界面了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [vue3使用element-plus](https://blog.csdn.net/m0_66492535/article/details/128048780)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [vue使用elementPlus](https://blog.csdn.net/weixin_45049852/article/details/124863506)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [怎么在Vue3中正确使用ElementPlus,亲测有效,避坑](https://blog.csdn.net/qq_44017116/article/details/127713704)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值