Skip to content

插件

在Vue项目中,插件通常用来为 Vue 添加全局功能,我们通常会通过全局方法Vue.use()使用插件。插件相关概念及开发规范可以参考文档

这里主要记录如何实现一个全局loading插件,以此可以熟悉Vue中开发插件的详细过程。

实现

在项目目录src/components下新建VLoading文件夹,文件目录结构如下

├─VLoading
│  │  index.js
│  │  
│  └─src
│          loading.vue
│          main.js
  • 模板文件VLoading/src/loading.vue
查看代码
vue
<template>
  <transition name="loading-fade">
    <div v-show="visible" class="v-loading">
      <div class="mask" />
      <!-- loading bubble -->
      <div v-if="options.bubble" class="loading-bubble-wrapper">
        <div class="loading-bubble" />
      </div>
      <!-- 默认loading方式 -->
      <div v-else class="loading-content">
        <!-- loading.svg为需要引用的loading图案,可自行替换 -->
        <img class="loading-icon" src="./loading.svg">
        <p 
          v-show="options.text !== ''" 
          class="loading-text" 
          :style="{ 'color': options.color }"
        >
          {{ options.text }}
        </p>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  name: 'JiuLoading',
  data() {
    return {
      visible: false,
      options: {
        color: '', // loading文字颜色
        text: '',
        bubble: false // 是否切换loading类型为bubble
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 99;
  width: 100%;
  min-height: 100vh;
  background: rgba(0, 0, 0, 0.4);
}
.v-loading {
  .loading-content {
    width: 1.2rem;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 9999;
    .loading-icon {
      width: 100%;
      height: 1.2rem;
      animation-name: loading;
      animation-duration: 1s;
      animation-timing-function: linear;
      animation-delay: 0;
      animation-iteration-count: infinite;
    }
    .loading-text {
      width: 100%;
      text-align: center;
      height: 1.2rem;
      line-height: 1.2rem;
      font-size: 0.3rem;
      font-weight: 500;
    }
  }
}

@keyframes loading {
  from {
    transform: rotate(0);
  }
  to {
    transform: rotate(360deg);
  }
}

.loading-fade-enter-active,
.loading-fade-leave-active {
  transition: opacity 0.6s ease-in-out;
}
.loading-fade-enter,
.loading-fade-leave-to {
  opacity: 0;
}

/* loading bubble */
.loading-bubble-wrapper {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 9999;
}

.loading-bubble {
  width: 1.5rem;
  height: 0.5rem;
  position: relative;
}

.loading-bubble::before,
.loading-bubble::after {
  content: '';
  display: block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  position: absolute;
  transform: translate3d(0, 0, 0);
}

.loading-bubble::before {
  animation: loading-bubbleL 2s ease-in-out infinite;
  background: #ee6362;
}

.loading-bubble::after {
  animation: loading-bubbleR 2s ease-in-out infinite;
  background: #2cb0b2;
}

@keyframes loading-bubbleL {
  0% {
    left: 0;
    transform: scale(1.1);
  }
  50% {
    left: calc(100% - 0.5rem);
    transform: scale(1);
  }
  100% {
    left: 0;
    transform: scale(1.1)
  }
}

@keyframes loading-bubbleR {
  0% {
    left: calc(100% - 0.5rem);
    transform: scale(1.1);
  }
  50% {
    left: 0;
    transform: scale(1);
  }
  100% {
    left: calc(100% - 0.5rem);
    transform: scale(1.1)
  }
}
</style>
  • VLoading/src/main.js
js
import Vue from 'vue'
import Loading from './loading.vue'

// 创建构造函数
const LoadingConstructor = Vue.extend(Loading)

// 插件默认配置
const defaultOpt = {
  text: '',
  color: '',
  zIndex: 100
}

// 绑定原型方法--关闭
LoadingConstructor.prototype.close = function() {
  // 异步销毁
  setTimeout(() => {
    if (this.$el && this.$el.parentNode) {
      this.$el.parentNode.removeChild(this.$el)
    }
    // 销毁loading实例
    this.$destroy()
  }, 600)
  this.visible = false
  document.body.style.overflow = ''
}

// 核心方法
const loading = (options = {}) => {
  if (Vue.prototype.$isServer) return
  // 合并配置
  options = Object.assign({}, defaultOpt, options)
  // 创建实例
  const instance = new LoadingConstructor(options)
  // 挂载dom元素
  const vm = instance.$mount()
  document.body.appendChild(vm.$el)
  document.body.style.overflow = 'hidden'
  // 确保dom完全更新后显示组件
  Vue.nextTick(() => {
    instance.visible = true
    instance.options = options
    const maskEle = vm.$el.firstElementChild
    maskEle.style.zIndex = options.zIndex
  })

  // 返回实例,可在创建loading后调用instance.close关闭loading
  return instance
}

export default loading
  • VLoading/index.js
js
import loading from './src/main'

// 暴露对象,提供install方法
export default {
  install(Vue) {
    Vue.prototype.$loading = loading
  }
}
  • 注册插件src/main.js
js
import VLoading from '@/components/VLoading'

Vue.use(VLoading)

new Vue({
  // ...
})
  • 项目中使用
js
// 创建
const loading = this.$loading({
  bubble: true
})

// you can do something...

// 关闭
loading.close()

效果

Updated at: