Автоматическая перезагрузка изображений в компоненте Vue

У меня есть простой компонент для операций с товарами. В этом компоненте я могу создавать товары и менять их изображения. Когда я изменяю изображение определенного товара, серверное приложение выполняет замену существующего изображения с тем же именем, поэтому URL-адрес изображения не изменяется. В результате изображения на странице не перезагружаются, пока я не обновлю страницу.

Как заставить принудительно перезагрузить образы после получения успешного ответа сервера?

Хорошие составляющие источники:

<template>
  <div class="container">
    <h3 class="mt-5">Goods</h3>

    <div class="row mt-5">
      <div class="col-md-12">
        <div class="card">
          <div class="card-header align-middle">
            <div class="card-tools">
              <button class="btn btn-success btn-sm" v-b-modal.create-good-modal>Add</button>
            </div>
          </div>
          <div class="card-body table-responsive p-0">
            <table class="table table-hover text-center">
              <thead>
                <tr>
                  <th>ID</th>
                  <th>Image</th>
                  <th>Name</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="good in goods" :key="good.id">
                  <td class="align-middle">{{ good.id }}</td>
                  <td class="align-middle">
                    <img class="img-thumbnail" width="60" :src="goodImageUrl(good.image)"
                      v-b-modal.change-good-image-modal @click="changeGoodImage(good.id)" />
                  </td>
                  <td class="align-middle">{{ good.name }}</td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
    <b-modal ref="createGoodModal" id="create-good-modal" title="New good" hide-footer @show="resetCreateGoodModal"
      @hide="resetCreateGoodModal">
      <b-form @submit="createGood" class="w-100">
        <b-form-group id="form-create-good-name-group" label="Name" label-for="form-create-good-name-input">
          <b-form-input id="form-create-good-name-input" type="text" v-model="createGoodForm.name" required
            placeholder="Good name">
          </b-form-input>
        </b-form-group>
        <b-form-group id="form-create-good-image-group">
          <b-form-file v-model="createGoodForm.image" required :state="Boolean(createGoodForm.image)"
            placeholder="Choose good image">
          </b-form-file>
          <div class="mt-3">Selected image: {{ createGoodForm.image ? createGoodForm.image.name : '' }}</div>
        </b-form-group>
        <b-button type="submit" variant="primary">Create</b-button>
      </b-form>
    </b-modal>
    <b-modal ref="changeGoodImageModal" id="change-good-image-modal" title="Change good image"
      @hide="resetChangeGoodImageModal" hide-footer>
      <b-form @submit="updateGoodImage" class="w-100">
        <b-form-group id="form-change-good-image-group">
          <b-form-file v-model="changeGoodImageForm.image" required :state="Boolean(changeGoodImageForm.image)"
            placeholder="Choose good image">
          </b-form-file>
          <div class="mt-3">Selected file: {{ changeGoodImageForm.image ? changeGoodImageForm.image.name : ''
                      }}
          </div>
        </b-form-group>
        <b-button type="submit" variant="primary">Save</b-button>
      </b-form>
    </b-modal>
  </div>
</template>

<script>
  import {objectToFormData} from "object-to-formdata";
    export default {
    data() {
        return {
    goods: [],
          createGoodForm: new Form({
    name: "",
            image: null
          }),
          changeGoodImageForm: new Form({
    id: {},
            image: null
          })
        };
      },
      methods: {
    getGoods() {
    axios
      .get("goods/list")
      .then(response => {
        this.goods = response.data;
      })
      .catch(error => {
        console.error(error);
      });
        },
        createGood() {
    this.createGoodForm
      .post("goods", {
        transformRequest: [
          function (data, headers) {
            return objectToFormData(data);
          }
        ]
      })
      .then(() => {
        this.$refs.createGoodModal.hide();
        this.getGoods();
      })
      .catch(error => {
        console.log(error);
      });
        },
        changeGoodImage(id) {
    this.changeGoodImageForm.id = id;
        },
        updateGoodImage() {
    this.changeGoodImageForm
      .post("goods/" + this.changeGoodImageForm.id + "/image", {
        transformRequest: [
          function (data, headers) {
            return objectToFormData(data);
          }
        ]
      })
      .then(() => {
        this.$refs.changeGoodImageModal.hide();
        this.getGoods();
      })
      .catch(error => {
        console.log(error);
      });
        },
        goodImageUrl(filename) {
          return "http://127.0.0.1:8098/api/img/goods/" + filename;
        },
        resetCreateGoodModal() {
    this.createGoodForm.reset();
        },
        resetChangeGoodImageModal() {
    this.changeGoodImageForm.reset();
        }
      },
      created() {
    this.getGoods();
      }
    };
</script>

Всего 2 ответа


Мое предложение состоит в том, чтобы просто добавить случайный кеш-буфер к URL-адресу изображения - параметр запроса, который сервер проигнорирует, заставит браузер получить новое изображение. Это предполагает, что ваш сервер не отклонит запрос, но в большинстве случаев этот подход работает. Что-то вроде /myimage.png?cacheBuster=<somethingRandom> . Обратите внимание, что в приведенном ниже фрагменте новое изображение кошки фактически не будет загружено, поскольку на этом сервере каждый раз не появляется новое изображение . Это просто демонстрация.

Чтобы уменьшить технический долг и помощь в ведении журнала / отладке, я бы предложил немного проделать дополнительную работу и попросить ваш API вернуть новую версию образа, которую вы можете добавить вместо простого перебора кеша. Но это большой проект. Пример /myimage.png?version=2

const originalSource = 'https://upload.wikimedia.org/wikipedia/commons/thumb/6/69/June_odd-eyed-cat_cropped.jpg/120px-June_odd-eyed-cat_cropped.jpg'
var app = new Vue({
  el: '#app',
  data() {
    return {
      imageSource: originalSource
    }
  },
  methods: {
    bustThatCache() {
      this.imageSource = `${originalSource}?cacheBust=${Math.random()}`
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <p>Image Src:{{imageSource}}</p>
  <img :src="imageSource"></img>
  <button @click="bustThatCache">Load new image</button>
</div>


Я добавил следующий метод:

refreshImageCache(filename) {
  if (filename.includes('?')) {
    filename = filename.split('?')[0];
  }

  return filename += '?ts=' + new Date().getTime();
}

и использовать его для обновления данных следующим образом:

updateGoodImage() {
  this
    .changeGoodImageForm
    .post("goods/" + this.changeGoodImageForm.id + "/image", {
      transformRequest: [
        function (data, headers) {
          return objectToFormData(data);
        }
      ]
    })
    .then(() => {
      let good = this.tiles.find(g => g.id === this.editGoodImageForm.id);
      good.image = this.refreshImageCache(tile.image);

      this.$refs.editGoodImageModal.hide();
    })
    .catch(error => {
      console.log(error);
    });
}

Есть идеи?

10000