首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue3 TypeScript 全局 Message 提示框

Vue3 TypeScript 全局 Message 提示框

作者头像
玖柒的小窝
修改2021-10-08 11:20:46
2.5K0
修改2021-10-08 11:20:46
举报
文章被收录于专栏:各类技术文章~各类技术文章~

前言

实现过一个 vue2 + ts 下的全局的 Message 提示框。闲暇之余想在 vue3 + ts 的框架下也实现此功能。

技术思路

总思路.png
总思路.png

在 vue3 上的实现思路

流程图.png
流程图.png

install 函数是把编写的 Message 组件实例化并渲染到页面的关键。其中的步骤:

  1. 根据 Message 组件实例化一个 Message
  2. 在 document.body 上 appendChild 实例
  3. 同时返回一个 destory 函数用于手动销毁实例
  4. 倒计时时间到销毁实例

编写 Message 组件代码

// Mssage.vue
<template>
  <transition name="slide">
    <div class="message-wrap" :class="[type, center ? 'text-center' : '']" :style="{ ...style }" v-if="visible">
      <div v-if="messageArr.length" class="message-line">
        <div v-for="(item, index) in messageArr" :key="index" class="message-line-item">
          {{ item }}
        </div>
      </div>
      <div v-else class="message">{{ message }}</div>
    </div>
  </transition>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
  props: {
    message: {
      type: String,
      default: '',
    },
    type: {
      type: String,
      default: 'success',
    },
    duration: {
      type: Number,
      default: 2000,
    },
  },
  data() {
    const messageArr: Array<string> = [];
    const style = {};
    return {
      messageArr,
      visible: true,
      center: false,
      style,
    };
  },
  created(): void {
    const arr = this.message.split('\n');
    if (arr.length > 1) {
      this.messageArr = arr;
    }
  },
  mounted(): void {
    this.startTimer();
  },
  methods: {
    startTimer(): void {
      const { duration } = this;
      const timer = setTimeout(() => {
        this.visible = false;
        clearTimeout(timer);
      }, duration);
    },
  },
});
</script>
复制代码

编写 install 函数并挂载到 vue 全局

// index.ts
import { App, render, createVNode } from 'vue';
import Message from './Message.vue';

const defaultOpt = {
  // 创建默认参数
  duration: 2000,
  type: 'success',
};

// 消息数组
const stack: Array<HTMLDivElement> = [];

/**
 * @description: 销毁 body 上的 Message 实例
 * @param {HTMLDivElement} ele
 * @return {*}
 */
const removeContainer = (ele: HTMLDivElement): void => {
  const index = stack.findIndex((item) => item === ele);
  if (~index) {
    stack.splice(index, 1);
    setStyle();
  }
};
/**
 * @description: 把实例加到实例队列 stack 中,并设置队列中搜有实例的 style
 * @param {HTMLDivElement} ele
 * @return {*}
 */
const addContainer = (ele: HTMLDivElement): void => {
  stack.push(ele);
  setStyle();
};
/**
 * @description: 设置 stack 中所有实例的 style
 * @param {*}
 * @return {*}
 */
const setStyle = () => {
  stack.forEach((item, index) => {
    if (item?.getElementsByClassName('message-wrap')?.[0]) {
      let top = 0;
      if (index > 0) {
        top +=
          (stack[index - 1].getElementsByClassName('message-wrap')[0] as HTMLElement)?.getBoundingClientRect()
            ?.bottom || 0;
      }
      // eslint-disable-next-line
      (item.getElementsByClassName('message-wrap')[0] as HTMLElement).style.marginTop = `${top}px`;
    }
  });
};

// 创建挂载实例
// eslint-disable-next-line
const createMount = (opts: { [key: string]: any }) => {
  const { duration } = opts;
  // 创建一个 div 容器
  const container = document.createElement('div');
  // 创建 Message 实例,createVNode 的性能比 h 更好
  const vm = createVNode(Message, opts);
  // 把实例 render 到容器上
  render(vm, container);
  addContainer(container);
  // 把容器渲染到 body 上
  document.body.appendChild(container);
  const destory = () => {
    const timer = setTimeout(() => {
      render(null, container);
      removeContainer(container);
      document.body.removeChild(container);
      clearTimeout(timer);
    }, 500); // 500 为动画结束时间,根据情况修改
  };

  const timer = setTimeout(() => {
    destory();
    clearTimeout(timer);
  }, duration || defaultOpt.duration);

  return { destory };
};

function Toast(options: { message: string; duration?: number } | string): {
  destory: () => void;
} {
  if (typeof options === 'string') {
    // eslint-disable-next-line
    options = {
      ...defaultOpt,
      message: options || '',
    };
  } else {
    // eslint-disable-next-line
    options = {
      ...defaultOpt,
      ...options,
    };
  }
  return createMount(options);
}

Toast.install = (app: App<Element>) => {
  app.component('toast', Message);
  app.provide('Toast', Toast);
  // 挂载 Toast 为全局方法 $toast
  // eslint-disable-next-line
  app.config.globalProperties.$toast = Toast;
};

export default Toast;
复制代码

use 为全局组件;增加 $toast 声明,消除使用时的 ts 报错

import { createApp } from 'vue';
import Toast from '@/components/toast';

const app = createApp(App);
app.use(Toast);
app.mount('#app');

// 定义了全局方法之后需要扩充类型
declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $toast: typeof Toast;
  }
}
复制代码

使用

this.$toast('message');

this.$toast({
  message: 'message',
  dutation: 5000,
})
复制代码

和 vue2 上的对比

vue2vue3创建方式1. 先创建构造函数  const MessageBox = Vue.extend(MessageComp);  2. 通过构造函数实例化对象 const instance: any  = new MessageBox({ data: options, }).$mount();1. 先创建一个 div 容器 const container = document.createElement('div');   2. createVNode 创建组件实例 const vm = createVNode(Message, opts);  3. 把实例 render 到容器上 render(vm, container);

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 技术思路
  • 在 vue3 上的实现思路
  • 编写 Message 组件代码
  • 编写 install 函数并挂载到 vue 全局
  • use 为全局组件;增加 $toast 声明,消除使用时的 ts 报错
  • 使用
  • 和 vue2 上的对比
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档