如何实现自己的上传文件组件并在vue js中自定义?

How to implement my own upload file component and make it customizable in vue js?

我正在从事一个项目,该项目需要在很多地方实现具有不同样式的上传文件组件。我想创建高度可定制的组件,我可以轻松修改其设计,而且我不想重复自己,想将所有通用逻辑提取到一个地方。

此刻我有一个vue.js 2.2.2 版和bulma css 框架。我有这个组件的基本实现,只有一种设计可用。它支持代表当前上传状态的几种状态:

  1. 组件正在等待输入
  2. 上传开始
  3. 上传成功完成
  4. 上传失败

此组件还负责上传过程。

此时我的组件有很多职责: 1. 它知道如何处理状态:

<p v-if="isWaiting">
...
</p>

<div v-if="isLoading" class="is-loading">
    <p class="title">{{currentPercent}} %</p>
</div>

...
data() { return {
    currentStatus = Statuses.waiting
}}

computed: {
    isWaiting() {
      return this.currentStatus === Statuses.waiting;
    },
    ...
}
  1. 它知道如何上传数据并计算当前已传输数据的百分比:
selectedFileChanged: async function(event) {
      if (event.target.files.length === 0) return;

      this.currentStatus = Statuses.uploading;
      this.selectedFile = event.target.files[0];

      const formData = new FormData();
      formData.append("file", this.selectedFile);

      try {
        const result = (await axios.post("some url", formData, {
          onUploadProgress: progress => {
            const loaded = progress.loaded;
            const total = progress.total;
            this.currentPercent = Math.floor((loaded * 100) / total);
          }
        })).data;

        this.currentStatus = Statuses.uploaded;

        this.$emit("fileUploaded", {
          file: this.selectedFile,
          payload: result
        });
      } catch (exception) {
        this.currentStatus = Statuses.error;
      }
    }
  1. 它只有一种样式我可以使用

您可以在此处找到完整代码:https://codesandbox.io/s/vue-template-gz2gk

所以我的问题是:如何构建此组件以便有机会轻松更改其样式以及如何处理上传状态?

在我看来: 1.组件应该不知道axios是什么以及如何上传数据; 2. 组件应该只负责当前状态以及如何显示

我可以介绍一个新的上传服务,它将知道如何上传数据并为上传文件组件添加一个新的道具(当前状态)并从父组件更改它。但在这种情况下,我将为组件的所有实例编写相同的代码。

有人知道如何创建此类可自定义组件的最佳实践吗?

更新 1 我尝试使用插槽实现此功能并最终得到:https://codesandbox.io/s/vue-template-pi7e9

组件仍然知道如何上传数据,但现在我可以更改上传组件的样式。 所以新的问题是:如何使用插槽而不传输大量变量以及如何处理上传。我不想让我的组件知道如何上传数据:(

我已经按照以下方式完成了组件:

在我的父组件中,我的组件有两种不同的样式:

  <div id="app" class="container box">
    <upload-file url="url"></upload-file> <-- the default one with statuses outside -->

    <upload-file url="url"> <-- custom one which look like a box with statuses in it -->
      <template #fileselect="{status, change}">
        <custom-file-upload :status="status" @change="change"></custom-file-upload>
      </template> <-- I've used custom-file-upload component here and tnjected all the properties from default implementation -->
    </upload-file>
  </div>

我的默认文件输入只是一个带有默认实现的插槽:

<template>
  <div>
    <slot name="fileselect" :change="selectedFileChanged" :status="status">
      <input id="fileselect" @change="selectedFileChanged" class="file-input" type="file">

      <div class="help is-info" v-if="isWaiting">Waiting</div>
      <div class="help is-success" v-if="isUploaded">Uploaded</div>
      <div class="help is-info" v-if="isUploading">Uploading {{currentPercent}} %</div>
      <div class="help is-error" v-if="isFailed">Failed</div>
    </slot>
  </div>
</template>

代码是什么样的:

  name: "upload-file",

  props: ["url"], // url which will be used in upload request

  data() {
    return {
      currentStatus: 1,
      selectedFile: null,
      currentPercent: 0
    };
  },

computed: {
    someOtherProperties,
    status() {
      return {
        isWaiting: this.isWaiting, // this.currentStatus === 1
        isUploaded: this.isUploaded,
        isUploading: this.isUploading,
        isFailed: this.isFailed,

        currentPercent: this.currentPercent,
        selectedFile: this.selectedFile
      };
    }
  },

 methods: {
    selectedFileChanged: function(event) {
      this.selectedFile = event.target.files[0];

      const xhr = new XMLHttpRequest();

      // some handlers for XHR

      xhr.open("POST", this.url, true);

      xhr.send(formData);
    }
  }

现在我可以使用具有不同样式的文件上传组件,但它会将所有状态处理和上传逻辑封装在一个基本实现中。

我希望这个解决方案对某人有所帮助:)

查看完整版代码,请关注https://codesandbox.io/s/vue-template-pi7e9