Vue - 插件开发

插件通常用来为 Vue 添加全局功能,例如数据字典插件、上传插件等等。

一、使用插件

通过全局方法 Vue.use() 使用插件。它需要在你调用 new Vue() 启动应用之前完成:

// 调用 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)

new Vue({
  // ...组件选项
})

也可以传入一个可选的选项对象:

Vue.use(MyPlugin, { someOption: true })

Vue.use 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件,在像 CommonJS 这样的模块环境中,你应该始终显式地调用 Vue.use()。

当 install 方法被同一个插件多次调用,插件将只会被安装一次。

二、开发插件

Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:

MyPlugin.install = function (Vue, options) {
  // 1. 添加全局资源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 逻辑...
    }
    ...
  })

  // 2. 注入组件选项
  Vue.mixin({
    created: function () {
      // 逻辑...
    }
    ...
  })

  // 3. 添加实例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 逻辑...
  }
  // 4. 添加全局组件
  Vue.component('uploadplugin',upload) //upload为组件
}

注意:如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。

三、开发实例

参考一个上传插件:

import upload from './ajax-upload.vue'
let ajaxUpload = {}
// eslint-disable-next-line no-unused-vars
ajaxUpload.install = function (Vue, options) {
  Vue.component(upload.name, upload) // testPanel.name 组件的name属性
}
export default ajaxUpload

然后上传插件对应的组件:

<template>
  <div class="wt-upload">
    <div class="file-area">
      <div>
        <input type="file" name="file" id="file" class="file" @change="previewImage($event)" multiple/>
        <label for="file" class="file-label">选择文件</label>
      </div>
      <div v-show="options.imagePreview">
        <div class="img-preview" ref="imgPreview">
        </div>
      </div>
      <p class="mt-sm">
        <button id="upload" @click="uploadFile(file)" class="upload">上传</button>
      </p>
      <div class="progress-area" v-show="options.showProgress ? options.showProgress : false">
        <p class="mb-sm">进度显示:</p>
        <div class="progress">
          <div class="progress-bar" id="progress" ref="progress">0%</div>
        </div>
        <div>
          <p class="time" ref="time"></p>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'hupload',
  props: ['options'],
  data () {
    return {
      imgPreview: null,
      xhr: null,
      loaded: 0,
      ot: 0,
      total: 0,
      oloaded: 0,
      file: null
    }
  },
  components: {
  },
  mounted () {
    this.imgPreview = this.$refs.imgPreview
  },
  created () {
    this.xhr = new XMLHttpRequest()
  },
  methods: {
    uploadFile (file) {
      if (!file) {
        alert('请选择文件')
        return
      }
      if (this.options.limitSize) {
        if (file.files[0].size > (this.options.limitSize) * 1e6) {
          alert(`文件大小不得超过${this.options.limitSize}M`)
          return
        }
      } else {
        if (file.files[0].size > 10000000) {
          alert(`文件大小不得超过10M`)
          return
        }
      }
      if (!this.options.fileUploadName) {
        alert('请配置与后端约定上传的key值')
        return
      }
      if (!this.options.url) {
        alert('请配置与后端约定的上传接口地址')
        return
      }
      let formData = new FormData()
      formData.append(this.options.fileUploadName, file.files[0])
      this.xhr.onload = this.uploadSuccess
      this.xhr.upload.onprogress = this.setProgress
      this.xhr.onerror = this.uploadFailed
      this.xhr.open('post', this.options.url, true)
      this.xhr.send(formData)
    },
    previewImage (event) {
      this.file = event.target
      this.imgPreview.innerHTML = ''
      // 每次重新选择文件的时候,都会去除上次选择产生的img标签
      let isImg = (event.target.files[0].type).indexOf('image/') > -1
      if (isImg) {
        // 如果是图片 就解析图片预览
        let img = document.createElement('img')
        this.imgPreview.appendChild(img)
        let reader = new FileReader()
        reader.onload = function (event) {
          img.src = event.target.result
          img.width = '200'
        }
        reader.readAsDataURL(event.target.files[0])
      } else {
        console.log('为文件选择一个默认的logo')
      }
    },
    setProgress (event) {
      let progress = this.$refs.progress
      // event.total是需要传输的总字节,event.loaded是已经传输的字节。如果event.lengthComputable不为真,则event.total等于0
      if (event.lengthComputable) {
        this.loaded = event.loaded
        this.total = event.total
        let complete = (event.loaded / event.total * 100).toFixed(1)
        progress.innerHTML = Math.round(complete) + '%'
        progress.style.width = complete + '%'
      }
      // let time = document.getElementById('time')
      let time = this.$refs.time
      let nt = new Date().getTime() // 获取当前时间
      let pertime = (nt - this.ot) / 1000
      // 计算出上次调用该方法时到现在的时间差,单位为s
      this.ot = new Date().getTime() // 重新赋值时间,用于下次计算
      let perload = event.loaded - this.oloaded
      // 计算该分段上传的文件大小,单位b
      this.oloaded = event.loaded // 重新赋值已上传文件大小,用以下次计算
      // 上传速度计算
      let speed = perload / pertime // 单位b/s
      let bspeed = speed
      let units = 'b/s' // 单位名称
      if (speed / 1024 > 1) {
        speed = speed / 1024
        units = 'k/s'
      }
      if (speed / 1024 > 1) {
        speed = speed / 1024
        units = 'M/s'
      }
      speed = speed.toFixed(1)
      // 剩余时间
      let resttime = ((event.total - event.loaded) / bspeed).toFixed(1)
      resttime = resttime > 0 ? resttime : '0'
      time.innerHTML = '传输速度:' + speed + units + ',剩余时间:' + resttime + 's'
    },
    uploadSuccess () {
      if (this.xhr.readyState === 4 && this.xhr.status === 200) {
        setTimeout(() => {
          // 回调给父组件
          this.sendMsgToParent('success')
        }, 1000)
      }
    },
    uploadFailed (err) {
      console.log(err)
      this.sendMsgToParent({'error': err})
    },
    sendMsgToParent (msg) {
      this.$emit('receiveUploadMsg', msg)
    }
  }
}
</script>

<!-- 公共的样式 -->
<style>
  .mb-sm {
    margin-bottom: 10px;
  }
  .mt-sm {
    margin-top: 10px;
  }
  .wt-upload {
    text-align: left;
  }
  .file-area {
    width: 80%;
    margin: 0 auto;
  }
  .file-area  .file {
    display: none;
  }
  .wt-upload  .file-label {
    display: block;
    width: 100px;
    padding: 8px;
    background: #39D2B4;
    color: #fff;
    font-size: 1em;
    transition: all .4s;
    cursor: pointer;
    text-align: center;
  }
  .wt-upload .file-label:hover {
    background: rgb(123, 219, 200);
  }
  .wt-upload .file-label:focus {
      background: rgb(32, 148, 125);
  }
  .wt-upload .img-preview {
    margin-top: 20px;
    margin-bottom: 20px;
  }
  .wt-upload  .upload,.wt-upload .abort {
    display: inline-block;
    width: 100px;
    padding: 8px;
    background: #39D2B4;
    color: #fff;
    font-size: 1em;
    transition: all .4s;
    cursor: pointer;
    outline: none;
    border: none;
  }
  .wt-upload  .upload:hover {
    background: rgb(123, 219, 200);
  }
  .wt-upload .upload:focus {
    background: rgb(32, 148, 125);
  }
  .wt-upload .progress-area {
    padding: 20px;
  }
  .wt-upload .progress {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    height: 1rem;
    overflow: hidden;
    font-size: 0.75rem;
    background-color: #e9ecef;
    border-radius: 0.25rem;
  }
  .wt-upload  .progress-bar {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    -ms-flex-direction: column;
    flex-direction: column;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    color: #fff;
    text-align: center;
    background-color: #007bff;
    transition: width 0.6s ease;
  }
  .wt-upload .time {
    margin-top: 10px;
  }
</style>
View Code

main.js里面安装插件:

import fileupload from './plugin/fileupload/ajax-upload.js'
Vue.use(fileupload)

插件使用:

<template>
    <div>
        <h2>插件开发测试</h2>
        <hupload :options=options v-on:receiveUploadMsg="receiveUploadMsg"> </hupload>
    </div>
</template>

<script>
/*import Vue from 'vue'
 import hupload from '../plugin/components/ajax-upload.js'
Vue.use(hupload) */
export default {
    data () {
        return {
            options: {
                'showProgress': true,
                'imagePreview': true,
                'url': 'str',
                'fileUploadName': 'ajax-upload',
                'limitSize': 1
            }
        }
    },
    components: {
    },
    methods: {
        receiveUploadMsg (msg) {
            console.log(msg)
        }
    }
}
</script>

<style>
</style>
View Code

参考:

https://cn.vuejs.org/v2/guide/plugins.html#%E4%BD%BF%E7%94%A8%E6%8F%92%E4%BB%B6

https://cn.vuejs.org/v2/api/#Vue-use


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值