【问题标题】:Don't update component until image loaded在加载图像之前不要更新组件
【发布时间】:2020-12-02 12:37:35
【问题描述】:

我有一个 CurrentItem 组件,它显示了某个项目的图像和描述。该项目会根据来自服务器的事件定期更改。我遇到的问题是,当它加载时,新项目的图像需要一些时间来加载,在此期间组件只有一个很大的空白区域,图像应该去,这看起来不太好。

我宁愿继续显示旧项目,直到加载新项目的图像,然后才更新组件。有没有简单的方法来实现这一点?

一些示例代码:

就问题而言,您可以将项目视为简单的 JSON 结构:

{
  "id": "ABC123",
  "description": "A fine yellow specimen",
  "image": "//example.com/images/ABC123.jpeg"
}

通过 WebSocket 来自服务器 JSON 的事件,如下所示:

{ "nextItemId": "ABC124" } 

当我们收到其中一个时,我们会发送一个 GET 请求以在 //example.com/items/ABC124.json 获取下一个项目。

【问题讨论】:

  • 您能否显示代码,说明您在何处/如何从服务器接收事件以及其中包含哪些信息?一些简单的例子使演示更容易(它是否接收原始图像数据?一个 url?一个现有数据集的索引?)

标签: vue.js


【解决方案1】:

这是我自己的最佳解决方案。

基本上我的想法是在 DOM 中有另一个 <CurrentItem /> 副本,其中包含 display:none; 并包含即将成为当前项目的项目。然后,当加载该组件中的图像时,我们会更新实际的 CurrentItem。像这样的:

<template>
  <CurrentItem
    :key="currentItemId"
    :item="currentItem"
  />
  <CurrentItem
    :key="aboutToBecomeCurrentItemId"
    :item="aboutToBecomeCurrentItem"
    @imageLoaded="onImageReady"
    style="display:none;"
  />
</template>

<script>
// ...

export default {
  // ...

  data() {
    return {
      currentItem = null,
      aboutToBecomeCurrentItem = null
    },

    methods: {
      onItemUpdated(newItem) {
        this.aboutToBecomeCurrentItem = newItem;
      },
  
      onImageReady() {
        this.currentItem = this.aboutToBecomeCurrentItem;
      }
    }
  }

  // ...
}
</script>

我认为这会起作用,但如果有更简单或更惯用的方法来实现同样的效果,我很想知道。

【讨论】:

    【解决方案2】:

    预加载图像并等待其onload 事件触发,然后再设置item

    methods: {
       onReceived(newItem) {
          const image = new Image();  // Create new empty image
          image.onload = () => {      // CALLBACK:  Will fire when image is done loading
             this.item = newItem;         // Will set the current item
             this.isLoading = false;      // Will stop animation
          }
          this.isLoading = true;      // Start animation
          image.src = newItem.image;  // Begin loading 
       }
    }
    

    完成后,将设置当前项目。这是一个带有大图的演示:

    Vue.component('item', {
      props: ['item'],
      template: `
      <div>
        <div>ITEM (id: {{ item.id }})</div>
        <div>{{ item.description }}</div>
        <div><img :src="item.image" ref="img" width="100" height="100" /></div>
      </div>
      `
    })
    
    new Vue({
      el: "#app",
      data() {
        return {
          isLoading: false,
          item: {
            "id": "ABC123",
            "description": "A fine yellow specimen",
            "image": "https://cdn.iconscout.com/icon/free/png-256/google-470-675827.png"
          }
        }
      },
      methods: {
        simulateEvent() {
          this.onReceived({
            "id": "ZYX987",
            "description":  "A funky jaundiced specimen",
            "image": "https://upload.wikimedia.org/wikipedia/commons/f/fa/Very_Large_Array_in_New_Mexico.jpg"
          });
        },
        onReceived(newItem) {
          this.isLoading = true;
          const image = new Image();
          image.onload = () => {
            this.item = newItem;
            this.isLoading = false;
          }
          image.src = newItem.image;
        }
      }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    <div id="app">
      <item :item="item"></item>
      
      <!-- Simulating events -->
      <button @click="simulateEvent">
      Simulate event
      </button>
      <span v-if="isLoading">[ Loading... ]</span>
    </div>

    【讨论】:

      【解决方案3】:

      你可以设置一个超时功能,直到图像加载

       .. codes to load new image
       setTimeout(function() {
            ... your old image codes
            ... 5000 means 5Seconds
        },5000);
      .. codes to display new image
      

      【讨论】:

      • 这不是我描述的问题的解决方案。我特别说我想继续展示旧项目,直到新项目准备好。
      猜你喜欢
      • 2016-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-12
      相关资源
      最近更新 更多