Vue組件化開發之拍照上傳組件【插件化】

1、前言

由於最近作H5項目,須要拍照上傳照片的功能。vue

因而就寫了一個傳統的Vue組件,功能實現都沒問題,但是用着用着發現了問題:element-ui

  • 1.傳統組件引用須要先import,而後放到components,而後放在template,很繁瑣。
  • 2.項目不一樣,UI設計不一樣,須要改樣式代碼。

多個項目都有拍照上傳功能,可是界面樣式都不同,每次還要臨時改樣式。後端

因此本着偷懶的原則,把這個組件拍照上傳到客戶端這部分功能UI給單獨分離開出來,單獨寫成一個插件,即用即插。數組

優勢:瀏覽器

  • 1.和UI分離,單獨使用,保留核心功能,提升了複用性。
  • 2.使用形式爲this.$useUpload,十分方便,無需像組件引入那樣,便捷性大大提高!

2、開始編寫上傳組件

2.1使用jsx語法更加簡潔

// upload-basic.vue
export default {
  name: 'UploadBasic',
  methods: {
      _changeFile(e) {
        const file = e.target.files[0];
        const reader = new FileReader();
        const self = this
        
        reader.onloadend = function() {
          const result = this.result;
          const img = new Image();
          img.src = result;
          img.onload = function() {
            self.$el.appendChild(img)
          }
        }
        
        reader.readAsDataURL(file);
      }
  }
  render() {
      return (
        <input ref="upBIuput" type="file" accept="image/*" onChange={(e) => {this._changeFile(e)}}/> ) } } 複製代碼

上面代碼很簡單,主要就是在頁面上渲染一個input元素,這個input用於文件的上傳,在手機上點擊後的效果就是喚醒系統的相機和相冊(2選1)bash

當你上傳圖片,或者拍照上傳後,就會觸發onChange事件,經過e.target.files[0]就能夠拿到當前上傳的文件(這裏指照片)。服務器

接着我使用了一個FileReader去讀取文件的內容,將文件格式轉換成base64位的形式,而後賦值到imgsrc屬性上,當圖片加載完畢後,經過this.$el.appenChild()方法把img這個DOM元素渲染到頁面上進行圖片的預覽。app

這樣就實現了拍照上傳到瀏覽器而且預覽,而後後續能夠經過把file塞到formData對象中上傳到後端服務器(這部份內容不提)。框架

2.2 把input隱藏掉

由於咱們通常上傳都有一個很好看的UI界面對吧,不須要這個醜醜的input,因此咱們只須要加一句樣式代碼去隱藏這個input。函數

<input ref="upBIuput" style="display:none;" type="file" accept="image/*" onChange={(e) => {this._changeFile(e)}}/>
複製代碼

這樣按鈕就隱藏了,並且不佔文檔流,可是按鈕是存在的。

把按鈕隱藏了,咱們怎麼觸發它的上傳事件呢?

很簡單,由於咱們使用了一個ref屬性,能夠去獲取到input的DOM對象。咱們只須要模擬按鈕點擊事件便可:

this.$refs.upBInput.click()
複製代碼

咱們把這句話包裹一層函數,須要的地方調用便可。

methods: {
  upload() {
    this.$refs.upBInput.click()
  }  
}
複製代碼

咱們寫好一個漂亮的上傳UI界面後,綁定一個click事件爲this.upload()就能夠觸發上傳了。

這樣效果也達到了,可是仍是沒有和UI分離,引入的時候也須要引入這個組件。想達到一開始說的效果怎麼辦呢?

3、抽離上傳功能

這就要用到咱們Vue中的插件功能了。

另起一個文件,以index.js命名。

// index.js
import Vue from 'vue'
import uploadComponent from './upload-basic.vue'

// 使用Vue構造器去建立一個上傳組件的子類
const uploadBasic = Vue.extend(uploadComponent)
// 建立一個div
const createDiv = () => document.createElement("div")

function useUpload(callback) {
  // 建立一個上傳組件類的實例化對象,這個uploadVm你能夠認爲就是上面那個組件
  const uploadVm = new uploadBasic()
  // 掛載目標
  let uploadEl = createDiv()
  // 將實例化對象掛載到uploadEl中
  uploadVm.$mount(uploadEl)
  // 觸發上傳操做,這個upload()方法就是咱們剛剛封裝的
  uploadVm.upload()
  // 綁定一個finish事件去通知咱們上傳完成了
  uploadVm.$on('finish', data => {

    uploadVm.$nextTick(() => {
      // 上傳完成後,作銷燬工做
      uploadVm.$destroy()
      uploadEl = null
    })
    // 經過callback回調函數將咱們須要的內容回調出去,供外部環境使用
    if(callback) callback(data)
  })
}

// 註冊這個插件,掛載到prototype上
export default {
  install: function (Vue, options) {
    Vue.prototype.$useUpload = function () {
      // [].splice.call(類數組對象, 0) 這個方法是將一個類數組對象拷貝一份轉換成一個Array
      // 經過...解構變成這個函數的參數
      useUpload(...([].splice.call(arguments, 0)))
    }
  }
}
複製代碼

如今咱們定義好了一個上傳組件的插件,可是咱們組件中並沒與去emit一個叫finish的事件,因此咱們須要回到組件中去寫這個。

// upload-basic.vue
// 重構一下這個函數
_changeFile(e) {
    const file = e.target.files[0];
    const reader = new FileReader();
    const self = this
    
    reader.onloadend = function() {
      // 這個this指代的是reader實例
      // result就是一個base64格式的文本
      const result = this.result;
      
      // 當reader實例讀取文件完畢後,我們把這個result和file都emit出去
      self.emit('finish', {
        base64: result,
        file: file
      })
    }
    
    reader.readAsDataURL(file);
  }
複製代碼

如今,咱們的組件upload-basic.vue和插件index.js都寫好了,將它們放到一個uploadPlugin文件夾中。

4、在main.js中引用

// main.js
import uploadPlugin from './uploadPlugin/index'

Vue.use(uploadPlugin)
複製代碼

接着到須要的頁面中:

// 業務1.vue
<template>
  <div>
    <h2 @click="handleClick">點擊上傳</h2>
    <img src="imgUrl" />
  </div>
</template>
<script>
export default {
 data() {
   return {
     imgUrl: ''
   }
 },
 methods: {
    handleClick() {
      this.$useUpload(data => {
        this.imgUrl = data.base64;
        console.log('文件:', data.file)
      }) 
    }
 },
}
</script>
複製代碼

以上這個例子,當你點擊「上傳文件」這個h2標籤時,就會去調用咱們封裝好的上傳組件,而後放到img中顯示,後續要上傳到後端也能夠。

這樣很是方便的,你就能夠在Vue組件中任意去調用這個全局的方法去使用上傳功能,經過回調函數中的data對象能夠拿到上傳文件的file和base64。

5、優勢

就如我剛開始說的那樣。

  • 1.和UI分離,單獨使用,保留核心功能,提升了複用性。
  • 2.使用形式爲this.$useUpload,十分方便,無需像組件引入那樣,便捷性大大提高!

6、後話

  • 1.其實咱們經常使用的element-ui中彈窗,提示等組件都有這種調用的形式,不知道小夥伴們發現沒有?(包括其餘UI框架)
  • 2.它們實現的原理都是經過Vue.extend()構造的,不信你能夠去看看它們的源碼。
  • 3.咱們一塊兒完成的這個上傳組件其實功能還十分的簡單,小夥伴們能夠自行擴展完善。
  • 4.移動端拍照上傳,圖片會莫名的旋轉,不是咱們想要的那樣,因此後面還須要將角度不正常的圖片旋轉過來而後給用戶預覽,否則體驗十分差勁。(我已經寫好了,嘿嘿。)
  • 5.後面更新,放上實際真機運行效果給你們看看。
相關文章
相關標籤/搜索