Chrome中HTML5 <audio>随机停滞的Svelte音频指南应用问题
解决Chrome中Svelte音频元数据加载随机停滞的问题
问题场景
我在开发基于Svelte的音频指南应用,需要展示播放列表内每首音频的时长,但无法预先获取该数据。当前的处理逻辑是:
- 为每首曲目创建隐藏的
<audio>元素,设置preload="metadata"加载元数据 - 获取到时长后,将该
<audio>元素从DOM中移除
使用的代码如下:
{#if !loadedMetadata} <!-- svelte-ignore a11y-media-has-caption --> <audio hidden preload="metadata" bind:duration on:loadedmetadata={() => loadedMetadata = true} on:stalled={() => console.log((id + 1) + ' stalled')}> <source src={url} type="audio/mpeg"> </audio> {/if}
现在遇到的问题:该逻辑在Firefox、Safari中运行正常,但Chrome里这些音频元素会随机出现加载停滞的情况。且这并非Chrome的「最多加载6个HTML5 audio标签」限制导致——有时仅5个元素就全停滞,有时11个元素却能正常加载。
几个可行的解决办法
1. 用JS Audio对象替代DOM中的<audio>元素
不需要将音频元素挂载到DOM中,直接在JavaScript层面创建Audio实例加载元数据,可避开DOM相关的潜在加载问题:
<script> import { onMount } from 'svelte'; let duration = null; let loadedMetadata = false; export let url; export let id; onMount(() => { const audio = new Audio(url); audio.preload = 'metadata'; // 加载完成后处理 const handleLoaded = () => { duration = audio.duration; loadedMetadata = true; // 清理资源与事件监听 audio.pause(); audio.src = ''; audio.removeEventListener('loadedmetadata', handleLoaded); audio.removeEventListener('stalled', handleStalled); }; // 停滞时触发重试 const handleStalled = () => { console.log((id + 1) + ' stalled, retrying...'); setTimeout(() => audio.load(), 1000); }; audio.addEventListener('loadedmetadata', handleLoaded); audio.addEventListener('stalled', handleStalled); // 手动触发加载 audio.load(); // 组件销毁时清理资源 return () => { audio.pause(); audio.src = ''; audio.removeEventListener('loadedmetadata', handleLoaded); audio.removeEventListener('stalled', handleStalled); }; }); </script> <!-- 时长展示区域 --> {#if loadedMetadata} <p>时长: {Math.floor(duration / 60)}:{Math.floor(duration % 60).toString().padStart(2, '0')}</p> {:else} <p>加载中...</p> {/if}
2. 限制并发加载的音频数量
虽然你排除了Chrome的6个并发限制,但Chrome对媒体资源的加载可能存在隐性调度规则,控制同时加载的音频数量能降低停滞概率:
- 维护一个加载队列,每次仅允许4-6个音频同时加载元数据
- 完成一个音频的元数据加载后,再从队列中取出下一个开始加载
3. 手动触发加载+超时重试
Chrome中部分场景下preload="metadata"可能无法自动触发加载,手动调用load()并添加超时重试逻辑:
<script> let audioElement; let loadedMetadata = false; let duration; export let url; export let id; // 元素渲染后手动触发加载 $: if (audioElement && !loadedMetadata) { audioElement.load(); } </script> {#if !loadedMetadata} <!-- svelte-ignore a11y-media-has-caption --> <audio hidden preload="metadata" bind:this={audioElement} bind:duration on:loadedmetadata={() => loadedMetadata = true} on:stalled={() => { console.log((id + 1) + ' stalled, retrying...'); setTimeout(() => audioElement?.load(), 1000); }}> <source src={url} type="audio/mpeg"> </audio> {/if}
原理说明
Chrome对DOM中的媒体元素存在特殊的资源调度机制,使用JS层面的Audio对象绕开DOM挂载,可避免部分DOM相关的加载阻塞问题;限制并发加载和添加重试逻辑,则能直接应对Chrome中随机出现的加载停滞情况。
内容的提问来源于stack exchange,提问作者Moon Rat
相关产品推荐
相关产品推荐

