异步渲染el-table表格图片

279 阅读2分钟

这是公司的一个业务接口返回的数据格式

image.png

这是一个用户反馈信息的列表,这里用到的主要数据是files_img这个数组,这里面放了反馈的图片数据列表,还需要根据这个图片id去请求另一个接口(暂称为B)拿到返回的base64图片数据.

至于为什么不是一个正常的url地址,种种原因吧,如果是正常的url地址,也就不会有这篇文章了

如果正常按逻辑去把数据放入el-table你会发现在表格中的所有图片数据没有拿到B接口的返回结果之前,什么都不显示,白屏.也就是请求B接口阻塞了表格中其他数据的渲染.这显然不行.

解决方案

1.先让图片的加载是异步的,不阻塞表格中其他数据的显示 2.控制请求B接口的并发数量(本例是3个) 3.在请求B接口的过程中加上loading,优化体验

<el-table-column label="图片" min-width="150">
    <template #default="scope">
      <div v-if="imageLoadingMap[scope.row.uuid]" class="image-loading">
        <el-icon class="is-loading">
          <Loading />
        </el-icon>
      </div>
      <div v-else-if="imageMap[scope.row.uuid]" style="display: flex; flex-wrap: wrap;">
        <el-image v-for="(img, index) in imageMap[scope.row.uuid]" :key="index" :preview-teleported="true"
          style="width: 30px; height: 30px; margin-right: 5px;" :src="img.url"
          :preview-src-list="imageMap[scope.row.uuid].map(i => i.url)" :fit="'cover'" />
      </div>
      <span v-else>-- --</span>
    </template>
</el-table-column>

// B接口的请求结果,key是这一行的uuid,value是一个对象,记录这对应请求会来的base64数据
const imageMap = ref<Record<string, { url: string }[]>>({});
// 用来控制请求B接口过程中的loading显示
const imageLoadingMap = ref<Record<string, boolean>>({});

onMounted(() => {
  getFbList();
});

const getFbList = async () => {
  const res = (await getFeedbackApi(state)) as any; // getFeedbackApi拿到的结果如文章开始展示的图片
  if (res) {
    feedbackList.value = res.data;
    total.value = res.total;
    res.data.forEach((item: any) => {
      useFormatImage(item);
    });
  }
};

// 修改图片处理函数
const useFormatImage = async (row: any) => {
  if (!row.files_img || !row.files_img.length) return;
  imageLoadingMap.value[row.uuid] = true; // 先加载loading
  try {
    // 通过map遍历files_img将对B接口的请求存在一个数组里
    const tasks = row.files_img.map((img: string) => async () => {
      const res = await getBase64File(img) as string; //getBase64File 接口 B
      return { url: res };
    });
   
    const fileList = await concurrentRequest(tasks, 3); // 控制并发数
    imageMap.value[row.uuid] = fileList; // 将base64数据存在对应的行中
  } catch (error) {
    console.error('Error loading images:', error);
  } finally {
    imageLoadingMap.value[row.uuid] = false; // 关闭loading
  }
};

// 并发控制函数
const concurrentRequest = async (tasks: any[], maxConcurrent = 3) => {
  const results = [];
  const executing = new Set();

  for (const task of tasks) {
    const promise = Promise.resolve().then(() => task());
    results.push(promise);
    executing.add(promise);
    const clean = () => executing.delete(promise);
    promise.then(clean).catch(clean);
    if (executing.size >= maxConcurrent) {
      await Promise.race(executing);
    }
  }
  return Promise.all(results);
};

注意到:在useFormatImage函数中我使用的是map,其实一开始使用的是forEach,但后来查资料才知道forEach是不支持异步循环的

image.png