Vue2 TypeScript 全局Message 提示框

1,927 阅读1分钟

前言

做过一个用 vue2 + typescirpt 搭建的移动端 H5 项目。在项目中需要使用到全局的与 Element UI 的 Message 组件类似的全局的提示组件。所以就实现了一个上述功能的全局 Message 组件。

技术思路

总思路.png

在 vue2 上的实现

流程图.png

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

  1. 根据 Message 组件生成其构造函数
  2. 实例化一个 Message 组件
  3. 在 document.body 上 appendChild 实例

编写 Message 组件代码

// Message.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 { Component, Vue } from 'vue-property-decorator';
@Component({
  components: {},
})
export default class extends Vue {
  private message = ''; // 消息内容
  private messageArr: Array<string> = [];
  private visible = false; // 是否显示组件
  private type = 'info'; // 'success','error'
  private duration = 2000; // 定时器时长
  private center = false; // 文本是否居中
  private style = {}; // 自定义 style
  created(): void {
    const arr = this.message.split('\n');
    if (arr.length > 1) {
      this.messageArr = arr;
    }
  }
  private startTimer() {
    window.setTimeout(() => {
      this.visible = false;
    }, this.duration);
  }
  private mounted() {
    this.startTimer();
  }
}
</script>

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

// install.ts
import Vue from 'vue';
import MessageComp from './Message.vue';

const MessageBox = Vue.extend(MessageComp); // 创建的是一个组件构造器,不是实例
let hasInsatll = false;
const Message = {
  install: (options: any): void => {
    if (typeof options === 'string' || typeof options === 'number') {
      options = {
        message: options,
      };
    }  else {
      if (typeof options !== 'object') {
        options = {
          message: '',
        };
      }
    }
    if (typeof options.duration !== 'number' || options.duration < 0) {
      delete  options.duration;
    }
    // 创建 Message 实例
    const instance: any  = new MessageBox({
      data: options,
    }).$mount();
    if (!hasInsatll) {
      hasInsatll = true;
    } else {
      // 把实例挂载到 body 上
      document.body.appendChild(instance.$el); // 添加dom元素
    }
    Vue.nextTick(() => { // dom元素渲染完成后执行回调
      // 显示 Message 组件实例
      instance.visible = true;
    });
  },
};

// 把 install 函数挂载到 vue 全局
Vue.prototype.$message = Message.install;

// 扩展 success 、error 两个函数
['success', 'error'].forEach((type) => {
  Vue.prototype.$message[type] = (message: any) => Vue.prototype.$message({ message, type });
});
export default Message;

Message use 为全局组件

// main.ts
import Message from '@/components/Message';

Vue.use(Message as any);

在 Vue 的 ts 声明中增加 $message ,消除使用时的 ts 报错

// shims-tsx.d.ts
import Vue, { VNode } from 'vue';

declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode {}
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [elem: string]: any
    }
  }
}

declare module 'vue/types/vue' {
  interface Vue {
    $message: any;
  }
}

使用

// 基础使用
this.$message('info type'); // info 消息类型
this.$message.success('success type'); // success 消息类型
this.$message.error('error type'); // error 消息类型

// 自定义属性值
this.$message({
    message: 'message',
    type: 'info' | 'success' | 'error', // info 为默认
    duration: 5000, // 单位毫秒
})