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

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

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

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

message.vue






组件的基本样式这里不进行讲解,我们把重点放到 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 测试显示组件





```

你可能感兴趣的:(Vue,前端,vue.js)