跳到正文

公共 API 与事件

Vue 组件和 useAudioPlayer() 都适配同一套 @trsoliu/audio-core 状态。组件事件不是 原生 <audio> 事件的逐条转发,而是根据不可变 AudioSnapshot 的变化发出。业务代码应 以事件 payload 或 composable 的 snapshot 为准,不要同时维护另一套播放状态。

导出边界

vue-audio-native 导入:

导出类型用途
VueAudioNativevalue推荐的 Vue 3 组件
AudioPlayervalueVueAudioNative 的同一组件别名
default exportVue Pluginapp.use(VueAudioNative),注册 VueAudioNativeAudioPlayer
useAudioPlayer()function无界面的响应式播放器 composable
detectAudioCapabilities()function无 UA 的运行时能力检测
formatMediaTime()function把秒数格式化为 mm:sshh:mm:ss
VueAudioNativePropsUseAudioPlayerOptionsUseAudioPlayerResulttypeVue 适配层类型
core 共享类型typetrack、snapshot、error、Bridge、handle 等类型

完整 type-only 导出为:

ts
import type {
  AudioPlayerSize,
  UseAudioPlayerOptions,
  UseAudioPlayerResult,
  VueAudioNativeProps,
  AudioArtwork,
  AudioBridgeEvent,
  AudioControllerOptions,
  AudioInput,
  AudioPlayerBridge,
  AudioPlayerError,
  AudioPlayerErrorCode,
  AudioPlayerHandle,
  AudioRuntimeCapabilities,
  AudioSnapshot,
  AudioSource,
  AudioSourceInput,
  AudioTrack,
  BufferedRange,
  PlaybackState,
  PreloadMode,
  RepeatMode,
} from 'vue-audio-native'

createAudioController()normalizeInput()normalizeSources()AudioControllerError 只从 @trsoliu/audio-core 导出,Vue 包不会重新导出这些底层 value:

ts
import { createAudioController } from '@trsoliu/audio-core'
import { VueAudioNative, useAudioPlayer } from 'vue-audio-native'

输入模型

AudioSourceAudioSourceInput

ts
interface AudioSource {
  src: string
  type?: string
}

type AudioSourceInput = string | AudioSource

src 可以接收一个 URL、一个带 MIME type 的 source,或它们的有序数组。空 URL 会被 忽略,同一 URL 只保留第一次出现的位置。提供 type 时,内核优先选择 HTMLMediaElement.canPlayType() 支持的 source;播放失败后会继续尝试后续 source。

AudioTrack

字段类型必填说明
idstring调用方维护的稳定曲目标识
sourcesAudioSource | readonly AudioSource[]一个或多个有序回退源
titlestring标题与 Media Session metadata
artiststring艺术家与 Media Session metadata
albumstring专辑与 Media Session metadata
artworkMediaImage[]默认封面与 Media Session artwork
downloadNamestring覆盖组件级下载文件名
peaksreadonly number[]调用方提供的波形峰值;播放器不会下载音频计算峰值

非空 tracks 优先于 srcsrc 又优先于兼容 prop url。同时传入多个输入时,Vue 组件会在开发环境给出警告。无可用 source 的 track 会被过滤;单独的 src 会被规范化 为 id: 'audio-source' 的单曲播放列表。

<VueAudioNative> props

输入与播放

prop类型默认值行为
tracksreadonly AudioTrack[]播放列表;非空时覆盖 srcurl
srcAudioSourceInput | readonly AudioSourceInput[]单曲或多格式回退输入
urlstring''0.x 兼容输入,仅在 trackssrc 都不可用时生效
autoplaybooleanfalse挂载或换源后尝试播放;浏览器阻止时发出可恢复的 AUTOPLAY_BLOCKED
preload'none' | 'metadata' | 'auto''metadata'写入原生 audio 的 preload 策略
volumenumber1有限值限制到 01;非有限初始值使用 1,后续非有限值忽略
mutedbooleanfalse静音状态
playbackRatenumber1有限值限制到 0.254;非有限初始值使用 1,后续非有限值忽略
repeatMode'off' | 'one' | 'all''off'不循环、单曲循环或列表循环
waitBufferbooleantruetrue 允许跳到已知 duration 内任意位置;false 把 seek 限制到当前最大缓冲终点

协作与浏览器集成

prop类型默认值行为
exclusivebooleanfalse仅当两个实例都开启且 group 相同时,新播放实例才暂停另一个实例
groupstring'default'互斥分组;空白字符串按 'default' 处理
mediaSessionbooleanfalse显式开启锁屏 metadata、播放状态与媒体按键处理
nativeControlsboolean见说明显式传入时决定是否强制原生 controls;未传时沿用旧 showControls

如果运行环境缺少自定义控件所需能力,即使 nativeControlsfalse 也会安全降级到 原生 controls。组件本身不暴露 bridge prop;宿主 Bridge 请使用 useAudioPlayer({ bridge }),或直接使用 core controller。

展示与兼容 props

prop类型默认值行为
size'small' | 'default' | 'large''default'写入根节点 data-size,切换包内尺寸样式
showCurrentTimebooleantrue显示当前时间与 duration
showVolumebooleantrue显示静音按钮和音量滑杆
showDownloadbooleantrue有 source 时显示下载链接;浏览器决定是否支持 download 和跨域下载
downloadNamestring''默认下载文件名;track 的同名字段优先
hintstring'暂无有效音频...'没有可播放 track 且未提供插槽时显示
showControlsbooleanfalse0.x 兼容项;仅在未显式传 nativeControls 时决定是否使用原生 controls

现代组件事件

事件按 snapshot 变化发出,不等同于同名 DOM 事件。Vue 模板中建议使用 kebab-case 监听名:

事件payload触发条件与注意事项
statechange(snapshot: AudioSnapshot)每次 PlaybackState 真正变化时触发,包括 loadingbufferingerror
ready(snapshot: AudioSnapshot)状态进入 ready 时触发;通常来自暂停时的 canplay,加载阶段的 play() 被拒绝后也会进入这一可重试状态;不是 loadedmetadata 的别名
play(snapshot: AudioSnapshot)状态进入 playing 时触发;从 buffering 恢复也会再次触发
pause(snapshot: AudioSnapshot)状态进入 paused 时触发,包括命令暂停和同组互斥暂停
ended(snapshot: AudioSnapshot)状态进入 ended 时触发;单曲循环、列表自动前进或列表循环时不会先发出终止事件
timeupdate(currentTime: number, snapshot: AudioSnapshot)snapshot 的 currentTime 变化时触发;播放中也会由进度帧同步,频率可能高于原生 timeupdate
trackchange(track: AudioTrack | null, trackIndex: number)当前 track 的索引或对象发生变化时触发;初始选择请直接读取 snapshot,不要依赖一次“挂载事件”
error(error: AudioPlayerError)出现新的公开错误时触发;多 source 回退只在没有后续可尝试 source 后报告媒体错误
vue
<VueAudioNative
  :tracks="tracks"
  @statechange="onStateChange"
  @timeupdate="(seconds, snapshot) => saveProgress(seconds, snapshot.track?.id)"
  @trackchange="(track, index) => console.log(index, track?.id)"
  @error="(error) => console.error(error.code, error.message)"
/>

AUTOPLAY_BLOCKED 会触发 error,但 snapshot 通常保持 readypaused,用户仍可通过 手势再次调用 play()。因此不要把所有 error 都当作播放器永久不可用。

0.x 兼容事件

事件名Vue 监听写法payload与现代事件的关系
on-change@on-change(playing: boolean)进入 playingtrue;进入 pausedendedfalse
on-timeupdate@on-timeupdate(currentTime: number)与现代 timeupdate 同时发出,但不包含 snapshot
on-metadata@on-metadata(event: Event)原生 loadedmetadatadurationchange;一次换源可能触发多次
on-audioId@on-audio-id(id: string)组件挂载后发出一次,如 vue-audio-native-1

兼容事件只为迁移保留。新代码优先使用现代事件和 AudioSnapshot,尤其不要用 on-change 推断 loadingbuffering 或错误状态。

AudioSnapshot

每次通知提供一个新的、顶层冻结的 snapshot:

字段类型说明
statePlaybackState当前状态,见下表
trackAudioTrack | null当前规范化 track
trackIndexnumber当前索引;没有 track 时为 -1
currentTimenumber非负播放位置,单位秒
durationnumber | null有效非负时长;未知、Infinity 或无效时为 null
bufferedreadonly BufferedRange[]{ start, end } 秒区间列表
volumenumber01
mutedboolean是否静音
playbackRatenumber当前倍速
repeatModeRepeatMode当前循环模式
errorAudioPlayerError | null最近公开错误;成功播放或重新加载后清空
state含义
idle尚未附着媒体元素,或当前没有可用输入
loading已选择 source,等待媒体就绪
ready暂停且可重试;通常已收到 canplay,也可能刚从加载阶段的播放拒绝恢复
playing正在播放
paused已暂停
buffering播放期间收到 waitingstalled
ended播放列表到达终点且没有继续循环
error所有可用 source 均失败,或发生其他致命媒体错误

结构化错误

ts
interface AudioPlayerError {
  code: AudioPlayerErrorCode
  message: string
  mediaErrorCode?: number
  cause?: unknown
}
code常见来源建议处理
AUTOPLAY_BLOCKEDplay() 被浏览器的用户手势策略拒绝显示播放按钮,让用户手势重试
SOURCE_NOT_SUPPORTED没有可用 source、原生错误码 4,或未附着媒体元素就播放更换 source、检查 MIME/type,或先挂载 audio
MEDIA_ABORTED原生媒体错误码 1允许重新加载或换源
NETWORK原生媒体错误码 2提供重试并检查 URL/CORS/网络
DECODE原生媒体错误码 3换用兼容编码或后备 source
UNKNOWN其他 play() 或媒体失败记录 cause,向用户提供重试或后备路径

AudioPlayerHandle

组件通过 defineExpose 暴露下列稳定句柄,composable 的 controls 使用同一接口:

方法返回值语义
play()Promise<void>请求播放;失败时以结构化 AudioControllerError 拒绝
pause()void暂停当前元素
toggle()Promise<void>根据原生元素的 paused 状态播放或暂停
stop()void暂停并跳回 0
seekTo(seconds)void忽略非有限值,并把位置限制到合法时长;waitBuffer=false 时不能超过最大缓冲终点
skipBy(seconds)void从当前时间相对跳转
selectTrack(index)Promise<void>选择合法整数索引;越界时抛出 RangeError,原先在播或 autoplay 时继续播放
previous()Promise<void>当前时间大于 3 秒时先回到本曲开头,否则切上一曲;all 可从首曲回绕
next()Promise<void>切下一曲;all 可从末曲回绕,否则进入 ended
setVolume(volume)void把有限数值限制到 01;忽略 NaN 与无穷值
setMuted(muted)void更新静音状态
setPlaybackRate(rate)void把有限数值限制到 0.254;忽略 NaN 与无穷值
setRepeatMode(mode)void更新 'off' | 'one' | 'all'
getElement()HTMLAudioElement | null获取当前原生元素;SSR 或未挂载时为 null
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VueAudioNative, type AudioPlayerHandle } from 'vue-audio-native'

const player = ref<AudioPlayerHandle | null>(null)
</script>

<template>
  <VueAudioNative ref="player" src="/episode.mp3" />
  <button type="button" @click="void player?.play()">播放</button>
</template>

useAudioPlayer()

ts
type UseAudioPlayerOptions = MaybeRefOrGetter<AudioControllerOptions>

interface UseAudioPlayerResult {
  audioRef: ShallowRef<HTMLAudioElement | null>
  snapshot: ShallowRef<AudioSnapshot>
  controls: AudioPlayerHandle
}

在模板中使用 ref="audioRef",不要写成普通 src 绑定:

vue
<script setup lang="ts">
import { computed } from 'vue'
import { useAudioPlayer } from 'vue-audio-native'

const source = computed(() => '/episode.mp3')
const { audioRef, controls, snapshot } = useAudioPlayer(() => ({
  src: source.value,
  bridge: null,
  exclusive: true,
  group: 'podcast',
}))
</script>

<template>
  <audio ref="audioRef" />
  <button type="button" @click="void controls.toggle()">
    {{ snapshot.state === 'playing' ? '暂停' : '播放' }}
  </button>
</template>

options 可以是普通对象、ref、computed 或 getter。composable 会响应输入和运行参数变化, 并在当前 effect scope 销毁时取消订阅、解绑媒体元素和释放 controller。bridge 可以传对象 或 null;移除旧 bridge 与换源同时发生时,旧宿主不会收到后续事件。

插槽

插槽props渲染条件
artwork{ track: AudioTrack }有当前 track 时;覆盖默认首张 artwork
before-controls{ controls, snapshot }使用自定义 controls 且有 track 时
after-controls{ controls, snapshot }使用自定义 controls 且有 track 时
tip没有可播放 track 时,优先于 slotTip
slotTip0.x 兼容 fallback;未提供 tip 时使用

直接使用 @trsoliu/audio-core

ts
import { createAudioController } from '@trsoliu/audio-core'

const controller = createAudioController({ src: '/episode.mp3' })
const unsubscribe = controller.subscribe((snapshot) => {
  console.log(snapshot.state, snapshot.currentTime)
})

controller.attach(document.querySelector('audio'))

subscribe() 不会立即回放当前值;需要初始值时先调用 getSnapshot()。listener 会在任何 snapshot 发布时收到完整值,不只是在 state 改变时收到。底层 AudioController 还提供:

方法说明
attach(element | null)绑定、换绑或解绑真实 HTMLAudioElement
destroy()移除监听器、进度帧、互斥注册和 Media Session 所有权;可重复调用
getSnapshot()同步读取当前不可变 snapshot
setInput({ src, tracks })替换输入并把选择重置到首个有效 track
subscribe(listener)订阅 snapshot,返回幂等取消函数
updateOptions(partial)合并运行选项;bridge: null 明确解绑宿主

Bridge Events

Bridge 是协议无关的单向出口:

ts
type AudioBridgeEvent =
  | { type: 'statechange'; snapshot: AudioSnapshot }
  | {
      type: 'trackchange'
      snapshot: AudioSnapshot
      track: AudioTrack | null
      trackIndex: number
    }
  | { type: 'error'; snapshot: AudioSnapshot; error: AudioPlayerError }
  • statechange 只在 snapshot.state 改变时发送。
  • trackchange 在当前 track/index 改变时发送;附着已有选中 track 的 controller 时也会 向当前 bridge 发送一次。
  • error 只在出现新的公开错误对象时发送。
  • bridge.emit() 抛出的宿主异常会被隔离,不会中断播放。

Bridge 不规定 WKWebView、Android JavascriptInterface 或 ArkWeb 的消息格式;宿主负责 序列化、鉴权和生命周期管理。

Core 工具函数

API行为
detectAudioCapabilities(element?)返回 custom controls、download、Media Session、原生 HLS、Pointer 和 touch 能力;SSR 时全部为 false
formatMediaTime(seconds)无效、负数或未知值返回 --:--,否则返回补零时间
normalizeSources(input)trim、过滤空 URL、按 URL 去重并保留顺序
normalizeInput(input)规范化 playlist;非空 tracks 优先,单 src 生成合成 track
AudioControllerErrorcode、可选 mediaErrorCodecause 的公开 Error 类

具体声明仍以已发布包的 .d.ts 为最终机器契约;发布门禁会验证 ESM、CJS、类型解析、 SSR 导入和 npm tarball 内容。