如何为循环遍历的每个clue项获取对应media数据,是否有更优实现方案
你现在的实现方式不合理,有两个核心问题:
- Vue 模板中的表达式会在每次组件重渲染时重复执行,把请求方法直接写在模板里会导致同一个clue的媒体接口被反复触发,造成不必要的性能浪费,甚至可能触发接口限流。
- 接口请求是异步操作,
fetchMediaClue直接返回的是Promise对象,模板无法直接解析出实际的媒体资源内容,渲染结果只会是[object Promise],根本达不到展示资源的需求。
接下来是推荐的实现方案:
方案1:全量预加载(适合clue数量较少的场景)
拿到clue列表接口的返回结果后,批量请求所有clue对应的媒体资源,挂载到clue对象上再统一渲染:
async function fetchClueList() { const chapterId = route.params.id // 先拉取clue列表 const { data: clueList } = await axios.get(`/chapters/${chapterId}/clues`) // 并发请求所有clue的媒体资源 await Promise.all(clueList.map(async (clue) => { const { data: mediaRes } = await axios.get(`/chapters/${chapterId}/clues/${clue.id}/media`) // 把媒体资源挂载到clue对象上 clue.mediaInfo = mediaRes })) this.clues = clueList }
模板直接读取挂载后的属性即可:
<div v-for="clue in clues" :key="clue.id"> <p>{{ clue.name }}</p> <div class="media-wrap"> <!-- 渲染图片 --> <img v-if="clue.mediaInfo.type === 'image'" :src="clue.mediaInfo.url" alt="资源图片"> <!-- 渲染音频 --> <audio v-if="clue.mediaInfo.type === 'audio'" :src="clue.mediaInfo.url" controls></audio> </div> </div>
方案2:按需懒加载(适合clue数量较多的场景)
如果clue列表很长,一次性批量请求所有媒体资源会占用过多带宽,改用滚动到可视区域后再加载对应资源的逻辑:
- 可以通过
IntersectionObserver监听clue元素是否进入视口,触发时再发起对应媒体接口请求 - 加载过程中显示占位loading,加载失败提供重试按钮
- 额外加一层本地缓存,已经请求过的clue媒体资源不用重复发起请求
可选优化
- 接口返回的媒体资源如果是临时签名地址,可以把缓存时效和地址有效期绑定,避免缓存过期导致资源无法访问
- 大体积的音频资源可以加分段加载配置,提升播放加载速度
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

