Small editor and colleagues found that El-upload had the following characteristics when they used it:

1. Set limit = 1. If the component is not destroyed, the upload method will be triggered only on the first upload and not on the second upload. The solution is to call the clearFile method.

2. AutoUpload is set to false and the beforeUpload call is not triggered.

3. HttpRequest no longer triggers beforeUpload after it is set.

4. Although we implement custom uploads by manually uploading files to the cloud, we also assign a value to the Action property

Some of these features can be inferred from the documentation, while others are more obscure. Read the source code below to reveal the principles behind these considerations

1. Module structure and reference relationship

Take a look at the overall structure of the el-Upload module:

Here’s how these files are referenced:

Index.js references index.vue under SRC

Index.vue references upload-list.vue and upload.vue

Upload. vue references upload-dragger.vue and Ajax

The parent-child aspect of the component looks like this:

2. Source code file analysis

Here’s a closer look at each file:

2.1 the index. Js file

import Upload from './src';

/* istanbul ignore next */
Upload.install = function(Vue) {
  Vue.component(Upload.name, Upload);
};

export default Upload;
Copy the code

All this file does is turn the Upload component into a component that can be registered by vUE.

2.2 Props,data, and so on in the index.vue file

2.2.1 Imported Files

First look at the imported file:

import UploadList from './upload-list';
import Upload from './upload';
import ElProgress from 'element-ui/packages/progress';
import Migrating from 'element-ui/src/mixins/migrating';
Copy the code

Two child components, UploadList and Upload, are imported and used in the Render method to assemble the final Upload component;

ElProgress is introduced and registered as a child component, but is not used in index and feels redundant;

When Migrating, you can migrate to or from a database when Migrating to or from a database. When Migrating, you can migrate to or from a database when Migrating to or from a database when Migrating to or from a database when Migrating to or from a database when Migrating.

for (var propName in definedProps) {
  propName = (0, _util.kebabCase)(propName); // compatible with camel case
  if (props[propName]) {
    console.warn('[Element Migrating][' + this.$options.name + '][Attribute]: '+ props[propName]); }}Copy the code

The second line of code here is interesting, (a,b)(c) what syntax is this? Understand the students can leave a message to give advice ~

Let’s look at how this file is used in index.vue:

getMigratingConfig() {
  return {
    props: {
      'default-file-list': 'default-file-list is renamed to file-list.'.'show-upload-list': 'show-upload-list is renamed to show-file-list.'.'thumbnail-mode': 'thumbnail-mode has been deprecated, you can implement the same effect according to this case: http://element.eleme.io/#/zh-CN/component/upload#yong-hu-tou-xiang-shang-chuan'}}; }Copy the code

When migrating, you can see from this code that this is used to indicate that some properties have been renamed or deprecated, so the English name “migrating” is fairly accurate.

2.2.2 properties props

Let’s look at index.vue below:

function noop() {}
Copy the code

Here we define a noop null method, which is used primarily as the default value for component function properties (props) :

 props: {
      onRemove: {
      type: Function.default: noop
    },
    onChange: {
      type: Function.default: noop
    },
 }
Copy the code

If I wrote the default value of the function myself, I might write it like this:

 props: {
      onRemove: {
      type: Function.default: () = >{}},onChange: {
      type: Function.default: () = >{}}},Copy the code

The arrow function is also pretty neat, but not as neat as noop

The props attribute defines the el-upload property, which defines everything we see in the document, as shown in the image below:

2.2.3 Use provide/ Inject for component value transfer

Here’s another code worth noting:

provide() {
  return {
    uploader: this
  };
},
Copy the code

If you are familiar with Vue, you will know that there are some deeply nested components, and the deep child component only needs part of the parent component. Provide /inject is a good choice. Here index.vue provides this to the child component. The main purpose is to enable child components to use some of the properties and methods defined in index.vue. To see how the components are used:

The upload – dragger. Vue:

inject: {
  uploader: {
    default: ' '}},onDrop(e) {
  if (this.disabled || !this.uploader) return;
  const accept = this.uploader.accept;
  this.dragover = false;
  if(! accept) {this.$emit('file', e.dataTransfer.files);
    return; }}Copy the code

Upload-dragger. vue uses the Accept attribute defined in index.vue. If you’re not familiar with provide and Inject, check out the vue documentation:

Cn.vuejs.org/v2/api/#pro…

V3.cn.vuejs.org/guide/compo…

2.3.4 data data

data() {
  return {
    uploadFiles: [].dragOver: false.draging: false.tempIndex: 1
  };
},
Copy the code

UploadFiles is a list of files that can be uploaded. DragOver and Draging are not used. TempIndex used to generate the uid of a file

handleStart(rawFile) {
   rawFile.uid = Date.now() + this.tempIndex++;
}
Copy the code

There are more methods defined in index.vue, so let’s take a look at each one

2.3 Methods in index.vue

2.3.1 handleStart

handleStart(rawFile) {
  rawFile.uid = Date.now() + this.tempIndex++;
  let file = {
    status: 'ready'.name: rawFile.name,
    size: rawFile.size,
    percentage: 0.uid: rawFile.uid,
    raw: rawFile
  };

  if (this.listType === 'picture-card' || this.listType === 'picture') {
    try {
      file.url = URL.createObjectURL(rawFile);
    } catch (err) {
      console.error('[Element Error][Upload]', err);
      return; }}this.uploadFiles.push(file);
  this.onChange(file, this.uploadFiles);
},
Copy the code

The handleStart method begins by further encapsulating the component’s files. RawFile is the file uploaded by the original user, and file is the encapsulated file. Assign the uni attribute to the initial file; The state is set to ready; Check whether listType is picture-card or picture, if it is picture, then assign the url attribute. Url.createobjecturl (rawFile) is the URL of the file created from rawFile.

Then place the file into uploadFiles and call the onChange method. OnChange is the component’s user-defined onChange method:

The on-change hook function holds a fileList that is the internal uploadFiles.

For url.createObjecturl, see developer.mozilla.org/zh-CN/docs/… In beforeDestroy, use the URL. RevokeObjectURL to release existing URL objects.

beforeDestroy() {
  this.uploadFiles.forEach(file= > {
    if (file.url && file.url.indexOf('blob:') = = =0) { URL.revokeObjectURL(file.url); }}); }Copy the code

2.3.2 handleProgress

handleProgress(ev, rawFile) {
  const file = this.getFile(rawFile);
  this.onProgress(ev, file, this.uploadFiles);
  file.status = 'uploading';
  file.percentage = ev.percent || 0;
},
Copy the code

HandleProgress is used to monitor the upload progress of the current file. First get the current file, then trigger the onProgress hook function passed in by the user, passing file to the hook function. Then set the file state to uploading and the percentage of files that are uploaded. I think it would be better semantically to put the last two sentences before this.onProgress.

So how does handleProgress listen for file upload status? How do you do that?

Let’s see the render function has this code:

const uploadData = {
  props: {
    'on-progress': this.handleProgress,
    'on-success': this.handleSuccess,
    'on-error': this.handleError,
    'on-preview': this.onPreview,
    'on-remove': this.handleRemove,
    'http-request': this.httpRequest
  },
  ref: 'upload-inner'
};
const uploadComponent = <upload {. uploadData} >{trigger}</upload>;
Copy the code

Assign handleProgress to the on-progress property of the Upload component. See how this property is used in the Upload component:

The upload. Vue:

export default {
	props: {
  	onProgress: Function,},// a post method defined in methods
  post(rawFile) {
    const { uid } = rawFile;
    const options = {
      headers: this.headers,
      withCredentials: this.withCredentials,
      file: rawFile,
      data: this.data,
      filename: this.name,
      action: this.action,
      onProgress: e= > {
        this.onProgress(e, rawFile);
      },
      onSuccess: res= > {
        this.onSuccess(res, rawFile);
        delete this.reqs[uid];
      },
      onError: err= > {
        this.onError(err, rawFile);
        delete this.reqs[uid]; }};const req = this.httpRequest(options);
    this.reqs[uid] = req;
    if(req && req.then) { req.then(options.onSuccess, options.onError); }}},Copy the code

As you can see from this code, the onProgress method defined in index.vue is called in the onProgress callback of the option object that sends the Ajax request, allowing you to monitor the percentage of files uploaded in real time.

2.3.3 handleSuccess and handleError

Similarly, handleSuccess and handleError can listen for file upload success and upload error for the same reason, by calling the handleSuccess and handleError methods in an Ajax callback.

 handleSuccess(res, rawFile) {
   const file = this.getFile(rawFile);

   if (file) {
     file.status = 'success';
     file.response = res;

     this.onSuccess(res, file, this.uploadFiles);
     this.onChange(file, this.uploadFiles); }},handleError(err, rawFile) {
   const file = this.getFile(rawFile);
   const fileList = this.uploadFiles;

   file.status = 'fail';

   fileList.splice(fileList.indexOf(file), 1);

   this.onError(err, file, this.uploadFiles);
   this.onChange(file, this.uploadFiles);
 },
Copy the code

HandleSuccess sets the file status to SUCCESS and triggers the onSuccess hook. HandleError sets the file state to fail, triggers the onError hook, and removes the file from the fileList.

2.3.4 handleRemove

Here’s handleRemove:

handleRemove(file, raw) {
  if (raw) {
    file = this.getFile(raw);
  }
  let doRemove = () = > {
    this.abort(file);
    let fileList = this.uploadFiles;
    fileList.splice(fileList.indexOf(file), 1);
    this.onRemove(file, fileList);
  };

  if (!this.beforeRemove) {
    doRemove();
  } else if (typeof this.beforeRemove === 'function') {
    const before = this.beforeRemove(file, this.uploadFiles);
    if (before && before.then) {
      before.then(() = > {
        doRemove();
      }, noop);
    } else if(before ! = =false) { doRemove(); }}},Copy the code

HandleRemove is used to delete files in some cases. A doRemove method is defined specifically for deleting files, which triggers the onRemove hook and calls abort (described below). If the beforeRemove hook function is not defined, it will be deleted directly. If the beforeRemove hook function is defined, beforeRemove will be called first and then decide whether to delete it. The description of before-remove in element-UI is as follows:

\

2.3.5 getFile

HandleRemove, handleError, handleSuccess, and handleProgress all use a method called getFile:

getFile(rawFile) {
  let fileList = this.uploadFiles;
  let target;
  fileList.every(item= > {
    target = rawFile.uid === item.uid ? item : null;
    return! target; });return target;
},
Copy the code

The getFile method mainly does the traversal, according to the UID judgment, return the target target.

2.3.6 abort

Abort method:

abort(file) {
  this.$refs['upload-inner'].abort(file);
},
Copy the code

This abort method is called in the doRemove of handleRemove, this is abort that calls the upload-inner, who is the upload-inner? See below:

Refs. upload-inner points to the upload component. Abort is the abort method defined in the upload file:

abort(file) {
  const { reqs } = this;
  if (file) {
    let uid = file;
    if (file.uid) uid = file.uid;
    if(reqs[uid]) { reqs[uid].abort(); }}else {
    Object.keys(reqs).forEach((uid) = > {
      if (reqs[uid]) reqs[uid].abort();
      deletereqs[uid]; }); }},Copy the code

The upload. Vue file contains the following code:

const req = this.httpRequest(options);
this.reqs[uid] = req;
if (req && req.then) {
  req.then(options.onSuccess, options.onError);
}
Copy the code

HttpRequest is an attribute exposed by el-upload (index.vue), which receives the method passed in by the user and passes it to the upload.vue file. HttpRequest defaults to Ajax if the user has not defined httpRequest:

import ajax from './ajax';
props: {
  httpRequest: {
    type: Function.default: ajax
  },
}
Copy the code

2.3.7 clearFiles

clearFiles() {
  this.uploadFiles = [];
},
Copy the code

The clearFiles method sets uploadFiles to null. This method is used by the user of el-upload:

2.3.8 submit

submit() {
  this.uploadFiles
    .filter(file= > file.status === 'ready')
    .forEach(file= > {
    this.$refs['upload-inner'].upload(file.raw);
  });
}
Copy the code

The submit method uploadFiles ready to upload to the cloud, and actually calls the method defined in upload.vue:

upload(rawFile) {
  this.$refs.input.value = null;
  // beforeUpload Does not exist to call the post method directly
  if (!this.beforeUpload) {
    return this.post(rawFile);
  }
	/ / call beforeUpload
  const before = this.beforeUpload(rawFile);
  // before exists and returns promise
  if (before && before.then) {
    before.then(processedFile= > {
      const fileType = Object.prototype.toString.call(processedFile);

      if (fileType === '[object File]' || fileType === '[object Blob]') {
        // Blob converts to
        if (fileType === '[object Blob]') {
          processedFile = new File([processedFile], rawFile.name, {
            type: rawFile.type
          });
        }
        for (const p in rawFile) {
          if(rawFile.hasOwnProperty(p)) { processedFile[p] = rawFile[p]; }}this.post(processedFile);
      } else {
        this.post(rawFile); }},() = > {
      // Remove the file
      this.onRemove(null, rawFile);
    });
  } else if(before ! = =false) {
    // before is a true value
    this.post(rawFile);
  } else {
    // Remove the file
    this.onRemove(null, rawFile); }},Copy the code

The upload method first checks whether there is beforeUpload. If not, the post method is directly called to upload the file. If so, determine whether the return value of before is true; if so, determine whether it is a promise; if not, remove the file. Take a look at the description of before-upload to understand:

2.4 render function in index.vue

Take a look at the render function (with the main parts preserved) :

render(h) {
    let uploadList;

    if (this.showFileList) {
      uploadList = (
        <UploadList// omit many attributes >
        </UploadList>
      );
    }

    const uploadData = {
      props: {
        type: this.type,
        drag: this.drag,
        // omit many attributes
        'on-preview': this.onPreview,
        'on-remove': this.handleRemove,
        'http-request': this.httpRequest
      },
      ref: 'upload-inner'
    };

    const trigger = this.$slots.trigger || this.$slots.default;
    const uploadComponent = <upload {. uploadData} >{trigger}</upload>;

    return (
      <div>{ this.listType === 'picture-card' ? uploadList : ''} { this.$slots.trigger ? [uploadComponent, this.$slots.default] : uploadComponent } {this.$slots.tip} { this.listType ! == 'picture-card' ? uploadList : ''}</div>
    );
  }
Copy the code

Check whether the showFileList attribute is passed in. If so, the uploadList variable is assigned to uploadList. Then I define uploadData to get the property and define ref; Get trigger and default slots, assign values to upload; If listType is picture-card, render uploadList. If listType is picture-card, render uploadList. If listType is picture-card, render uploadList. Determine whether there is trigger, if there is, render upload and slot, otherwise render upload; Render tip slot; UploadList = uploadList = uploadList = uploadList = uploadList = uploadList = uploadList = uploadList = uploadList

2.5 the upload. Vue analysis

2.5.1 props and data

The props defined in upload.vue are basically the same as the props defined in index.vue, and the reqs defined in data are used to hold ajax requests:

data() {
  return {
    mouseover: false.reqs: {}}; },Copy the code

Let’s look at the code referenced by reqs:

abort(file) {
  const { reqs } = this;
  if (file) {
    let uid = file;
    if (file.uid) uid = file.uid;
    if(reqs[uid]) { reqs[uid].abort(); }}else {
    Object.keys(reqs).forEach((uid) = > {
      if (reqs[uid]) reqs[uid].abort();
      deletereqs[uid]; }); }},post(rawFile) {
  const { uid } = rawFile;
  const options = {
    headers: this.headers,
    withCredentials: this.withCredentials,
    file: rawFile,
    data: this.data,
    filename: this.name,
    action: this.action,
    onProgress: e= > {
      this.onProgress(e, rawFile);
    },
    onSuccess: res= > {
      this.onSuccess(res, rawFile);
      delete this.reqs[uid];
    },
    onError: err= > {
      this.onError(err, rawFile);
      delete this.reqs[uid]; }};const req = this.httpRequest(options);
  this.reqs[uid] = req;
  if(req && req.then) { req.then(options.onSuccess, options.onError); }},Copy the code

Reqs is referenced in abort and POST methods. You can see that requests are removed from REQS when the upload succeeds and when the upload fails, so reQS definitely holds a reference to an incomplete request through the UID as the key.

2.5.2 isImage method

isImage(str) {
  return str.indexOf('image')! = = -1;
},
Copy the code

Determine whether it is an image by determining whether a string contains an image substring.

2.5.3 handleChange method

handleChange(ev) {
  const files = ev.target.files;

  if(! files)return;
  this.uploadFiles(files);
}
Copy the code

Upload a file when the file changes. The reason to fetch the file on ev.target.files is because the handleChange method is used on the native input:

2.5.4 uploadFiles method

uploadFiles(files) {
  if (this.limit && this.fileList.length + files.length > this.limit) {
    this.onExceed && this.onExceed(files, this.fileList);
    return;
  }

  let postFiles = Array.prototype.slice.call(files);
  if (!this.multiple) { postFiles = postFiles.slice(0.1); }

  if (postFiles.length === 0) { return; }

  postFiles.forEach(rawFile= > {
    this.onStart(rawFile);
    if (this.autoUpload) this.upload(rawFile);
  });
},
Copy the code

UploadFiles is used to upload multiple files. If limit is true and the number of files in the current fileList plus the number of files to upload is greater than limit, upload is forbidden. If the user defines an onExceed hook attribute, the hook is triggered. (2) Obtain all files and determine whether to upload one file or multiple files based on multiple. (3) Finally upload files one by one, calling onStart each time, which is the handleStart method defined in index.vue; If autoUpload is true, the Upload method is called, which in turn calls POST (mentioned several times above).

UploadFiles emits a file event from the upload-Dragger. vue subcomponent: uploadFiles emits a file event from the upload-dragger.vue subcomponent: uploadFiles emits a file event from the uploadFiles subcomponent.

// upload-dragger.vue
onDrop(e) {
  if (this.disabled || !this.uploader) return;
  const accept = this.uploader.accept;
  this.dragover = false;
  if(! accept) {this.$emit('file', e.dataTransfer.files);
    return;
  }
  / /... Omit some code
}
Copy the code

In the render function of upload.vue:

return (
  <div {. data} tabindex="0" >
    {
      drag
      ? <upload-dragger disabled={disabled} on-file={uploadFiles}>{this.$slots.default}</upload-dragger>
      : this.$slots.default
    }
    <input class="el-upload__input" type="file" ref="input" name={name} on-change={handleChange} multiple={multiple} accept={accept}></input>
</div>
);
Copy the code

Note that on-file={uploadFiles} is a listener for file events. So far, we can sort out the calling relationship of functions in upload.vue:

2.5.6 handleClick and handleKeydown methods

handleClick() {
  if (!this.disabled) {
    this.$refs.input.value = null;
    this.$refs.input.click(); }},handleKeydown(e) {
  if(e.target ! == e.currentTarget)return;
  if (e.keyCode === 13 || e.keyCode === 32) {
    this.handleClick(); }}Copy the code

HandleClick and handleKeydown are responses to mouse click events and keyboard keystrokes, respectively. HandleKeydown calls handleClick. See the following figure for usage:

The control keycode values used in this code are 13 for Enter and 32 for Spacebar:

More about keyboard events code value can refer to www.cnblogs.com/daysme/p/62…

2.5.7 render method

We have seen the code in the render method several times in the previous article, so I won’t paste it again. The main thing is that the content returned is the outer layer of the div package, and the inner layer is the upload-dragger and input

2.6 the upload – dragger. Vue analysis

The el-Upload component supports drag and drop uploads in this file.

First look at the template section:

<template>
  <div
    class="el-upload-dragger"
    :class="{ 'is-dragover': dragover }"
    @drop.prevent="onDrop"
    @dragover.prevent="onDragover"
    @dragleave.prevent="dragover = false"
  >
    <slot></slot>
  </div>
</template>
Copy the code

This uses the drag-and-drop API, described in chapter 20, section 6, of JavaScript Advanced Programming (4th edition). The following events are triggered in order when an element is dragged:

(1) dragStart

(2) computer

(3) the dragend

The following events are emitted when dragging to a valid drop target:

(1) dragenter

(2) the dragover

(3) Dragleave or drop

Upload-dragger uses drop, dragover, and dragleave, and defines methods to respond to these events:

onDragover() {
  if (!this.disabled) {
     this.dragover = true; }},Copy the code

OnDragover is used to change the styles, and the following styles are changed when the file is dragged over.

Upload-dragge does not define a special method for dragleave and sets dragover to false.

Next look at onDrop:

onDrop(e) {
  if (this.disabled || !this.uploader) return;
  const accept = this.uploader.accept;
  this.dragover = false;
  if(! accept) {this.$emit('file', e.dataTransfer.files);
    return;
  }
  this.$emit('file', [].slice.call(e.dataTransfer.files).filter(file= > {
    // Get the file name and type
    const { type, name } = file;
    // Get the file extension name
    const extension = name.indexOf('. ') > -1
    ? `.${ name.split('. ').pop() }`
    : ' ';
    const baseType = type.replace($/, / /. * ");
    return accept.split(', ')
      .map(type= > type.trim())
      .filter(type= > type)
      .some(acceptedType= > {
      / / extensions
      if (/.. + $/.test(acceptedType)) {
        return extension === acceptedType;
      }
      / / type
      if (//*$/.test(acceptedType)) {
        return baseType === acceptedType.replace($/, / / * ");
      }
      / / type
      if (+ / / ^ ^ /][^+ $/ /].test(acceptedType)) {
        return type === acceptedType;
      }
      return false;
    });
  }));
}
Copy the code

The main logic of this code includes:

(1) Return if the upload component is disabled or the Uploader is false.

(2) Otherwise, take the acceptable format, if the user does not define the format, upload directly; If the user defines an acceptable format, the file is checked one by one. Since the user may send multiple formats, you need to convert the comma-separated accept into an array, removing the Spaces before and after it, and keeping the true values (because the user may send ‘.doc,,.ppt’, where the Spaces between the two commas are false values).

(3) After processing the acceptable format, the file type or extension name and format are compared, and the file that conforms to accept is uploaded.

2.7 the upload – list. Vue analysis

Upload-list. vue main logic is in the template part, let’s look at it from the top down, from the outside to the inside:

 <li
      v-for="file in files"
      :class="['el-upload-list__item', 'is-' + file.status, focusing ? 'focusing' : '']"
      :key="file.uid"
      tabindex="0"
      @keydown.delete=! "" disabled && $emit('remove', file)"
      @focus="focusing = true"
      @blur="focusing = false"
      @click="focusing = false"
    >/ / content</li>
Copy the code

The outer layer is a for loop that generates li. This code mainly iterates through multiple files and generates a list of files. Note the bound keyboard event. Click delete to trigger the remove method, which corresponds to the code in index.vue, as shown below:

Inside li is the scope slot:

<slot :file="file">
  <img
       class="el-upload-list__item-thumbnail"
       v-if="file.status ! == 'uploading' && ['picture-card', 'picture'].indexOf(listType) > -1"
       :src="file.url" alt=""
       >
  <a class="el-upload-list__item-name" @click="handleClick(file)">
    <i class="el-icon-document"></i>{{file.name}}
  </a>
  <label class="el-upload-list__item-status-label">
    <i :class="{ 'el-icon-upload-success': true, 'el-icon-circle-check': listType === 'text', 'el-icon-check': ['picture-card', 'picture'].indexOf(listType) > -1 }"></i>
  </label>
  <i class="el-icon-close" v-if=! "" disabled" @click="$emit('remove', file)"></i>
  <i class="el-icon-close-tip" v-if=! "" disabled">{{ t('el.upload.deleteTip') }}</i> <! -- The close button will only display when li:focus, and will not be displayed after li: blur, so keyboard navigation will never focus on the close button -->
  <el-progress
               v-if="file.status === 'uploading'"
               :type="listType === 'picture-card' ? 'circle' : 'line'"
               :stroke-width="listType === 'picture-card' ? 6:2"
               :percentage="parsePercentage(file.percentage)">
  </el-progress>
  <span class="el-upload-list__item-actions" v-if="listType === 'picture-card'">
    <span
          class="el-upload-list__item-preview"
          v-if="handlePreview && listType === 'picture-card'"
          @click="handlePreview(file)"
          >
      <i class="el-icon-zoom-in"></i>
    </span>
    <span
          v-if=! "" disabled"
          class="el-upload-list__item-delete"
          @click="$emit('remove', file)"
          >
      <i class="el-icon-delete"></i>
    </span>
  </span>
</slot>
Copy the code

Images are rendered when the file is not uploaded on the web and the list-type of the file is picture or picture-card; El-progress is rendered when a file is being uploaded on the web. Display preview and Remove buttons if file list type is picture-card.

2.7 summarize

The whole el-Upload source code has been through, or quite harvest, know some of the external performance of el-Upload corresponding to the internal principle, in the future whether the use of components or secondary packaging can be done to know. Firstly, the file reference relationship between the whole modules of the el-Upload component is sorted out. Then the relevant methods of inde.vue and upload.vue are analyzed emphatically. Finally, the main logic of upload-dragger and upload-list.vue files is analyzed. Like my article can like forward, there are problems can leave a comment, you can also pay attention to the public account “review new knowledge” to get more front-end original articles ~