我正在参加「AdminJS·启航计划」
缘起
社区中有大量基于 vue 的UI组件库,比如移动端的vant
,PC端的element-ui
、ant-design-vue
等。但有时候这些组件库无法满足我们的开发需求,此时我们就需要自己写一个插件(组件)。
前段时间参与开发了一个大屏项目,横屏分辨率是3840*2160 px,引用的 element-ui 组件库,在高分辨率下各个组件的文字和大小都显得很小,比如 Message 组件。
本文是基于 vue2
自定义一个 toast 插件。之所以是插件而非组件,是因为我们要在全局代码中调用,是通过 js 代码调用该组件。
调研
首先是看下官方文档:
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
- 添加全局方法或者 property。如:vue-custom-element
- 添加全局资源:指令/过滤器/过渡等。如 vue-touch
- 通过全局混入来添加一些组件选项。如 vue-router
- 添加 Vue 实例方法,通过把它们添加到
Vue.prototype
上实现。 - 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:
MyPlugin.install = function (Vue, options) {
// 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// ...
}
}
通过全局方法 Vue.use() 使用插件,需要在调用 new Vue() 启动应用之前完成。
当 install 方法被同一个插件多次调用,插件将只会被安装一次。
Vue.use(MyPlugin)
new Vue({
// ...组件选项
})
这里我们要实现一个通用组件 toast
,可以通过this.$toast({ duration: 3000, message: 'xxx', icon: 'xxx' })
方法调用。
实现
/components/toast.vue
首先,编写 toast 插件的内容,包括 icon 图标、提示文字等。我们设置组件居中显示:
position: fixed;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
具体代码:
<template>
<div class="toast" v-show="isShow">
<i class="toast-icon">
<img v-if="icon" :src="icon" class="icon-image" alt="">
<img v-else src="~static/img/icon-warn.png" class="icon-image" alt="">
</i>
<div class="toast-text">{{message}}</div>
</div>
</template>
<script>
export default {
data() {
return {
isShow: false,
message: '',
icon: '',
duration: 2000,
};
},
mounted() {},
methods: {}
};
</script>
<style lang="scss" scoped>
.toast{
z-index: 2023;
position: fixed;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-sizing: content-box;
width: 300px;
max-width: 70%;
min-height: 0;
padding: 44px;
color: #fff;
font-size: 40px;
line-height: 55px;
white-space: pre-wrap;
text-align: center;
word-break: break-all;
background-color: rgba(0, 0, 0, 0.7);
border-radius: 12px;
.toast-icon{
font-size: 90px;
position: relative;
display: inline-block;
margin-bottom: 36px;
&:before {
display: inline-block;
}
.icon-image{
display: block;
width: 1em;
height: 1em;
object-fit: contain;
}
}
.toast-text{
font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
}
}
</style>
/plugins/toast.js
在这里编写并导出带有 install 方法的 Toast 对象。
首先,我们使用Vue.extend(showToast)
创建一个“子类”,生成 instance 实例。instance 实例 带有 toast 组件的参数,其中isShow 属性控制 toast 是否显示,message、icon 和 duration 是弹窗的具体参数。
然后,定义方法Vue.prototype.$toast
,将传参 options 对象和 {isShow: true} 赋值给 instance 实例,也就显示了 toast 弹窗。通过编写 toastTimer 定时器,经过 instance.duration 的时间间隔后设置 isShow = false,隐藏弹窗。
最后,新建 div 节点挂载 instance,并将 instance.$el 插入 document.body 即可。
instance.$mount(document.createElement('div'))
document.body.appendChild(instance.$el)
具体代码是:
import showToast from "@/components/toast.vue"
var inBrowser = typeof window !== 'undefined'
// 插件
let Toast = {
install: function (Vue) {
const showToastConstructor = Vue.extend(showToast)
const instance = new showToastConstructor()
Vue.prototype.$toast = (options = {}) => {
return new Promise((resolve, reject) => {
Object.assign(instance, options, {isShow: true})
instance.toastTimer && clearTimeout(instance.toastTimer)
instance.toastTimer = setTimeout(function () {
clearTimeout(instance.toastTimer)
Object.assign(instance, {isShow: false})
}, instance.duration)
resolve(true)
})
}
if (inBrowser) {
instance.$mount(document.createElement('div'))
document.body.appendChild(instance.$el)
}
}
}
export default Toast
/plugins/common.js
引入自定义插件:
import myToast from '~/plugins/toast.js'
Vue.use(myToast)
笔者使用了 Nuxt2 框架,还需要在 nuxt.config.js 中引入该文件:
plugins: [
'~/plugins/common.js',
],
尝试
到此,自定义插件已经实现了,试试效果:
this.$toast({
duration: 3000,
message: 'xxx'
})
后记
社区中有大量基于 vue 的UI组件库,但有时候这些组件库无法满足我们的开发需求,那么我们可以动手实现一个插件(组件)。本文是基于实际开发需求,使用 vue2 自定义了一个 toast 插件。