One, foreword

Because of the recent H5 project, I need the function of taking photos and uploading photos.

So I wrote a traditional Vue component, and everything was fine, but when I used it, I found a problem:

  • 1. Traditional component references need to import first, then place components, then template, which is tedious.
  • 2. UI design varies from project to project, and style code needs to be modified.

Many projects have photo uploading function, but the interface style is different, each time needs to change the style temporarily.

Therefore, in line with the principle of laziness, the function of taking photos of this component and uploading to the client is separated from the UI separately, and written as a plug-in, plug and use.

Advantages:

  • 1. It is separated from UI and used separately to retain core functions and improve reusability.
  • $useUpload, very convenient, no need to introduce components like that, convenience greatly improved!

Start writing the upload component

2.1 JSX syntax is more concise

// 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)}}/>
      )
  }
}
Copy the code

The above code is very simple, basically render an input element on the page, this input element is used for file upload, click on the phone to wake up the system camera and album (2 choose 1)

When you upload an image, or take a photo and upload it, the onChange event is triggered, and e.target.files[0] is used to retrieve the currently uploaded file (in this case, photos).

I then used a FileReader to read the contents of the file, converted the file format to Base64-bit, and assigned the value to the SRC attribute of img. When the image was loaded, The img DOM element is rendered to the page using the this.$el.appenchild () method to preview the image.

This allows the photo to be uploaded to the browser and previewed, which can then be subsequently uploaded to the back-end server by stuffing a file into a formData object (not to mention that).

2.2 Hide input

Because we usually upload a nice UI, right? We don’t need this ugly input, so we just need to add a style code to hide the input.

<input ref="upBIuput" style="display:none;" type="file" accept="image/*" onChange={(e) => {this._changeFile(e)}}/>
Copy the code

This way the button is hidden and does not occupy the document flow, but the button is there.

How do we trigger its upload event by hiding the button?

This is easy because we use a ref attribute to retrieve the INPUT DOM object. We just need to simulate the button click event:

this.$refs.upBInput.click()
Copy the code

We wrap this sentence around a layer of functions that can be called wherever needed.

methods: {
  upload() {
    this.$refs.upBInput.click()
  }  
}
Copy the code

Once we’ve written a nice upload UI, bind a click event to this.upload() to trigger the upload.

This is done, but it is not separated from the UI, so you need to introduce this component when you introduce it. What do you do if you want to achieve what you said at the beginning?

3. Disconnect the upload function

This is where the plug-in functionality in Vue comes in.

Create another file and name it index.js.

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

// Use the Vue constructor to create a subclass of the upload component
const uploadBasic = Vue.extend(uploadComponent)
// Create a div
const createDiv = (a)= > document.createElement("div")

function useUpload(callback) {
  // Create an instantiation object to upload the component class to uploadVm
  const uploadVm = new uploadBasic()
  // Mount the target
  let uploadEl = createDiv()
  // Mount the instantiated object to uploadEl
  uploadVm.$mount(uploadEl)
  // Trigger the upload operation, the upload() method we just wrapped
  uploadVm.upload()
  // Bind a Finish event to notify us that the upload is complete
  uploadVm.$on('finish', data => {

    uploadVm.$nextTick((a)= > {
      // After uploading, do the destruction
      uploadVm.$destroy()
      uploadEl = null
    })
    // use callback to callback the content we need for the external environment to use
    if(callback) callback(data)
  })
}

// Register the plugin and mount it to Prototype
export default {
  install: function (Vue, options) {
    Vue.prototype.$useUpload = function () {
      // [].splice. Call (Array-like object, 0) This method converts a copy of an array-like object into an Array
      / / by... Deconstruction becomes an argument to the functionuseUpload(... ([].splice.call(arguments.0)))}}}Copy the code

Now we have defined a plug-in to upload the component, but we do not emit an event called Finish in the component, so we need to go back to the component and write this.

_changeFile(e) {const file = e.targe.files [0]; const reader = new FileReader(); const self = this reader.onloadend =functionConst result = this.result; const result = this.result; // When the reader instance finishes reading the file, we emit the result and file self.emit('finish', {
        base64: result,
        file: file
      })
    }
    
    reader.readAsDataURL(file);
  }
Copy the code

Now that our component upload-basic.vue and plugin index.js are written, place them in a uploadPlugin folder.

4. Reference in main.js

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

Vue.use(uploadPlugin)
Copy the code

Then go to the desired page:

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(' file: ', data.file)})}},} </script>Copy the code

In the example above, when you click the H2 tag “Upload file”, it will call our wrapped upload component and display it in IMG, or upload it to the back end later.

Conveniently, you can call the global upload method anywhere in the Vue component, using the data object in the callback function to retrieve the file and Base64 of the uploaded file.

Five, the advantages of

As I said at the beginning.

  • 1. It is separated from UI and used separately to retain core functions and improve reusability.
  • $useUpload, very convenient, no need to introduce components like that, convenience greatly improved!

Sixth, the latter

  • 1. In fact, we commonly use element UI pop-ups, prompts and other components have this form of call, I wonder if you have noticed? (Including other UI frameworks)
  • 2. They are implemented by vue.extend () structure, do not believe you can go to see their source code.
  • 3. The function of the upload component we completed together is actually very simple, and friends can expand and improve it by themselves.
  • 4. When we take photos and upload them on the mobile terminal, the pictures will rotate inexplicably, which is not what we want. Therefore, we need to rotate the pictures with abnormal angles to give users a preview, otherwise the experience will be very poor. (I’ve already written it, heh heh.)
  • 5 behind the update, put on the actual real machine running effect for everyone to see.