博客音乐播放器实现详解
本文从零到一介绍博客中音乐播放器的搭建过程、技术原理、数据来源,以及如何切换歌单。
一、效果概览
博客底部固定显示一个迷你音乐播放器,支持:
- 播放/暂停/切歌
- 歌词滚动显示
- 播放列表展示
- 随机/顺序/单曲循环
- 音量调节
- 自动记住播放设置
二、技术选型
| 技术 | 作用 |
|---|---|
| APlayer | 开源 HTML5 音乐播放器,提供 UI 和播放控制 |
| Meting API | 第三方音乐聚合 API,支持网易云、QQ 音乐等 |
| Vue 3 组件 | 封装 APlayer 为 Vue 组件 |
| Pinia | 状态管理,存储网站配置(含歌单 ID) |
为什么选 APlayer?
APlayer 是目前最流行的前端音乐播放器库,具有:
- 开箱即用的精美 UI
- 支持歌词(LRC 格式)
- 吸底/迷你模式
- 完善的 API 和事件系统
- 体积小(~12KB gzipped)
三、组件架构
App.vue
└── <MusicPlayer> (总入口)
└── <aplayer> (APlayer 实例封装)
├── 获取歌单数据 (从 Meting API)
└── 创建 APlayer (渲染播放器)
3.1 入口组件 MusicPlayer/index.vue
vue
<template>
<aplayer
server="netease"
type="playlist"
:id="blog.blogInfoSafe.siteConfig.musicId"
:fixed="true"
theme="#e9546b"
/>
</template>
<script setup lang="ts">
import { useBlogStore } from "@/store";
import aplayer from "./aplayer.vue";
const blog = useBlogStore();
</script>
关键点说明:
| 属性 | 值 | 含义 |
|---|---|---|
server |
"netease" |
音乐来源为网易云音乐 |
type |
"playlist" |
类型为播放列表(歌单) |
:id |
musicId |
歌单 ID,从后台配置读取 |
:fixed |
true |
吸底固定模式 |
theme |
"#e9546b" |
播放器主题色(粉红色) |
3.2 APlayer 封装组件 MusicPlayer/aplayer.vue
这是核心组件,完成三件事:
- 定义 Props:接收播放器配置参数
- 请求歌单数据:调用 Meting API 获取歌曲列表
- 创建 APlayer 实例:传入容器和配置,渲染播放器
vue
<template>
<div ref="playerRef"></div>
</template>
<script setup lang="ts">
import APlayer from "aplayer";
import "aplayer/dist/APlayer.min.css";
// APlayer 歌曲信息结构
class Audio {
author: String; // 歌手
title: String; // 歌名
url: String; // 音频链接
pic: String; // 封面图
lrc: String; // 歌词
constructor(artist, name, url, cover, lrc) {
this.author = artist;
this.title = name;
this.url = url;
this.pic = cover;
this.lrc = lrc;
}
}
// 组件挂载后,请求歌单并创建播放器
onMounted(() => {
nextTick(() => {
request({
url: `https://api.i-meto.com/meting/api?server=${props.server}&type=${props.type}&id=${props.id}&r=${Math.random()}`,
method: "get",
}).then(({ data }) => {
instance = new APlayer({
container: playerRef.value,
fixed: props.fixed,
mini: props.mini,
autoplay: props.autoplay,
theme: props.theme,
loop: props.loop,
order: props.order,
preload: props.preload,
volume: props.volume,
mutex: props.mutex,
lrcType: props.lrcType,
listFolded: props.listFolded,
listMaxHeight: props.listMaxHeight,
storageName: props.storageName,
audio: data, // 歌曲数组
});
});
});
});
// 组件销毁时清理
onBeforeUnmount(() => {
instance.destroy();
});
</script>
Props 完整列表:
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
fixed |
Boolean | true |
吸底模式 |
mini |
Boolean | true |
迷你模式 |
autoplay |
Boolean | false |
自动播放 |
theme |
String | rgba(255,255,255,0.2) |
主题色 |
loop |
String | "all" |
循环模式(all/one/none) |
order |
String | "random" |
播放顺序(list/random) |
preload |
String | "auto" |
预加载策略 |
volume |
Number | 0.7 |
默认音量 |
server |
String | "netease" |
音乐平台 |
type |
String | "playlist" |
播放类型 |
id |
String | "186016" |
歌单 ID |
mutex |
Boolean | true |
互斥播放 |
lrcType |
Number | 3 |
歌词类型(3=LRC 文本) |
listFolded |
Boolean | true |
列表默认折叠 |
listMaxHeight |
String | "100px" |
列表最大高度 |
storageName |
String | "aplayer-setting" |
本地存储 key |
四、数据来源
4.1 歌单 ID 从哪来?
歌单 ID 存储在后台数据库 的 t_site_config 表中:
sql
-- 数据库字段
is_music INT -- 是否开启播放器(0否 1是)
music_id VARCHAR -- 网易云歌单 ID
对应 Java 实体:
java
// SiteConfig.java
@TableField("is_music")
private Integer isMusic;
@TableField("music_id")
private String musicId;
数据流向:
管理后台配置 musicId
→ PUT /admin/site/update (更新数据库 + 清除 Redis)
→ 用户访问博客 → GET / (获取 blogInfo)
→ 前端 Pinia store 存储 siteConfig.musicId
→ MusicPlayer 组件读取 musicId
→ 拼接 Meting API URL 请求歌单
4.2 Meting API 是什么?
Meting API 是一个免费的音乐聚合接口,支持多个平台:
| server 值 | 平台 |
|---|---|
netease |
网易云音乐 |
tencent |
QQ 音乐 |
kugou |
酷狗音乐 |
xiami |
虾米音乐 |
baidu |
百度音乐 |
请求格式:
GET https://api.i-meto.com/meting/api?server=netease&type=playlist&id=歌单ID&r=随机数
返回格式(JSON 数组):
json
[
{
"name": "歌名",
"artist": "歌手",
"url": "https://music.163.com/song/media/outer/url?id=xxx.mp3",
"cover": "https://p1.music.126.net/xxx.jpg",
"lrc": "[00:00.000] 歌词内容..."
},
// ...更多歌曲
]
4.3 如何获取网易云歌单 ID?
- 打开 网易云音乐
- 找到你喜欢的歌单
- 歌单页面 URL 形如:
https://music.163.com/#/playlist?id=186016 - 其中
186016就是歌单 ID
五、如何切换歌单
方法一:后台管理切换(推荐)
- 登录管理后台
- 进入 网站管理 → 网站配置 → 其他设置
- 找到「网易云歌单 ID」输入框
- 填入新的歌单 ID
- 保存
原理 :PUT /admin/site/update 更新数据库,同时删除 Redis 缓存。用户刷新页面后,GET / 返回最新配置,播放器自动加载新歌单。
方法二:前端代码切换音乐平台
如果想换成 QQ 音乐歌单,修改 MusicPlayer/index.vue:
vue
<aplayer
server="tencent" <!-- 改为 tencent -->
type="playlist"
:id="blog.blogInfoSafe.siteConfig.musicId" <!-- ID 改为 QQ 音乐歌单 ID -->
:fixed="true"
theme="#e9546b"
/>
目前的限制
- 歌单切换需要刷新页面才能生效(APlayer 实例只在
onMounted时创建一次) - 不支持前端动态切换多个歌单
server平台类型硬编码在index.vue中,无法从后台配置
如果想支持动态切换?
可以把 server 也加入 SiteConfig,然后前端 watch musicId 变化时销毁旧实例、创建新实例:
typescript
watch(() => props.id, () => {
if (instance) instance.destroy();
// 重新请求并创建
});
六、播放器样式定制
全局样式 common.scss
scss
/* 吸底模式下迷你播放器的宽度调整 */
.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
left: -66px !important;
}
.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover {
left: 0 !important;
}
这段 CSS 让播放器在迷你模式下默认隐藏部分内容,鼠标悬停时完整展示。
七、整体数据流图
┌─────────────────────────────────────────────────────────┐
│ 管理后台 (shoka-admin) │
│ 网站配置 → musicId: "186016" │
│ → PUT /admin/site/update │
└─────────────┬───────────────────────────────────────────┘
│ 更新 DB + 清除 Redis
▼
┌─────────────────────────────────────────────────────────┐
│ Spring Boot 后端 │
│ SiteConfig { isMusic: 1, musicId: "186016" } │
│ GET / → BlogInfoResp { siteConfig: { ... } } │
└─────────────┬───────────────────────────────────────────┘
│ 返回 JSON
▼
┌─────────────────────────────────────────────────────────┐
│ Vue 前端 (shoka-blog) │
│ Pinia Store → blog.siteConfig.musicId = "186016" │
│ │
│ MusicPlayer/index.vue │
│ └── <aplayer id="186016" server="netease" ...> │
│ │
│ MusicPlayer/aplayer.vue │
│ └── fetch Meting API → [歌曲数组] │
│ └── new APlayer({ audio: [歌曲数组], ... }) │
│ └── 🎵 播放器渲染完成! │
└─────────────────────────────────────────────────────────┘
│ HTTP GET
▼
┌─────────────────────────────────────────────────────────┐
│ Meting API (第三方服务) │
│ https://api.i-meto.com/meting/api │
│ ?server=netease&type=playlist&id=186016 │
│ 返回: [{ name, artist, url, cover, lrc }, ...] │
└─────────────────────────────────────────────────────────┘
八、常见问题
Q1: 播放器不显示?
- 检查后台
isMusic是否开启(当前代码未使用此字段判断,播放器始终显示) - 检查 Meting API 是否可访问:
https://api.i-meto.com/meting/api?server=netease&type=playlist&id=186016 - 检查浏览器控制台是否有 CORS 错误
Q2: 歌曲无法播放?
- 网易云部分歌曲有版权限制,Meting API 无法获取播放链接
- 尝试换一个包含更多无版权歌曲的歌单
Q3: 本地开发报错 Two output files share the same path?
- 原因:
global.d.ts中declare module "APlayer"大写,实际导入用小写"aplayer" - 解决:统一改为
declare module "aplayer",然后删除node_modules/.vite缓存
Q4: 想关闭播放器?
- 虽然后台有
isMusic开关,但当前代码未使用此字段 - 如需启用,在
index.vue添加v-if="blog.blogInfoSafe.siteConfig.isMusic"
九、涉及的文件清单
| 文件路径 | 作用 |
|---|---|
shoka-blog/src/components/MusicPlayer/index.vue |
播放器入口组件 |
shoka-blog/src/components/MusicPlayer/aplayer.vue |
APlayer 实例封装 |
shoka-blog/src/assets/styles/common.scss |
播放器样式覆盖 |
shoka-blog/src/types/global.d.ts |
APlayer 模块声明 |
blog-springboot/.../entity/SiteConfig.java |
数据库实体(musicId) |
blog-springboot/.../service/BlogInfoService.java |
返回博客配置信息 |
blog-springboot/.../service/SiteConfigService.java |
站点配置 CRUD + Redis |
十、总结
博客的音乐播放器由以下几个关键环节组成:
- 后台配置:管理员在网站配置中设置网易云歌单 ID
- 数据存储:歌单 ID 存在 MySQL + Redis 缓存
- 前端获取 :页面加载时通过
GET /获取配置 - 歌曲请求:通过 Meting API 将歌单 ID 转换为实际歌曲列表
- 播放器渲染:APlayer 接收歌曲数据,渲染吸底播放器
整个流程简洁高效,核心代码不到 200 行。如需扩展(如支持多歌单切换、多平台选择),只需在 SiteConfig 中增加相应字段并在前端 watch 变化即可。