还不会自定义一个全局 vue2 插件?

lxf2023-05-06 01:43:01

我正在参加「AdminJS·启航计划」

缘起

社区中有大量基于 vue 的UI组件库,比如移动端的vant,PC端的element-uiant-design-vue等。但有时候这些组件库无法满足我们的开发需求,此时我们就需要自己写一个插件(组件)。

前段时间参与开发了一个大屏项目,横屏分辨率是3840*2160 px,引用的 element-ui 组件库,在高分辨率下各个组件的文字和大小都显得很小,比如 Message 组件。

本文是基于 vue2 自定义一个 toast 插件。之所以是插件而非组件,是因为我们要在全局代码中调用,是通过 js 代码调用该组件。

调研

首先是看下官方文档

插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:

  1. 添加全局方法或者 property。如:vue-custom-element
  2. 添加全局资源:指令/过滤器/过渡等。如 vue-touch
  3. 通过全局混入来添加一些组件选项。如 vue-router
  4. 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
  5. 一个库,提供自己的 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 插件。