前言
做过一个用 vue2 + typescirpt 搭建的移动端 H5 项目。在项目中需要使用到全局的与 Element UI 的 Message 组件类似的全局的提示组件。所以就实现了一个上述功能的全局 Message 组件。
技术思路
在 vue2 上的实现
install 函数是把编写的 Message 组件实例化并渲染到页面的关键。其中的步骤:
- 根据 Message 组件生成其构造函数
- 实例化一个 Message 组件
- 在 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, // 单位毫秒
})