为 Astro Paper 引入 mdx 和 Aplayer 支持
花了大半个上午的时间,在 DeepSeek 的帮助下为 Astro Paper 主题引入了对 mdx 和 APlayer 嵌入式音乐播放器的支持。
效果
使用方法
在 mdx 文章中插入如下代码:
mdximport { APlayer } from "@/components/mdx"; <APlayer fixed={false} autoplay={false} audio={[ { name: "Song Name", artist: "Artist", url: "Song.mp3", cover: "Cover.png", lrc: "lrc.lrc", }, ]} />
为 Astro Paper 主题加入 mdx 支持
参考了 Astro 官方文档中的配置方法。
因为我的 pnpm 由于莫名其妙的符号链接问题炸了,所以这里使用 npm 手动配置。
bashnpm install @astrojs/mdx
然后在 src/astro.config.ts 中引入 mdx 并在 integrations 部分加入 mdx():
tsimport mdx from '@astrojs/mdx'; export default defineConfig({ site: SITE.website, integrations: [ sitemap({ filter: page => SITE.showArchives || !page.endsWith("/archives"), }), mdx() ],
最后在 src/content.config.ts 的内容集合 loader 中加入对 .mdx 扩展名的支持即可:
tsconst blog = defineCollection({ loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/data/blog" }), // 省略其他代码
引入 Aplayer.js 支持
首先使用 npm 安装 aplayer:
bashnpm install aplayer --save
然后,在 src 目录下新增 types 目录,在 src/types/aplayer.d.ts 中编写类型定义:
tsdeclare module "aplayer" { interface Audio { name: string; artist: string; url: string; cover: string; lrc: string; [key: string]: any; } interface APlayerOptions { container: HTMLElement; fixed?: boolean; autoplay?: boolean; mini?: boolean; theme?: string; loop?: "all" | "one" | "none"; order?: "list" | "random"; volume?: number; audio: Audio[]; } class APlayer { constructor(options: APlayerOptions); currentTime: number; paused: boolean; volume: number; play(): void; pause(): void; destroy(): void; } export default APlayer; } // 扩展 Window 类型 interface Window { APlayer: typeof import("aplayer").default; }
接着,在 src/layouts/Layout.astro 的 header 内加入如下代码,实现全局导入 APlayer。
astro<script> // @ts-ignore - 显式声明全局类型 import APlayer from 'aplayer'; // 通过类型断言解决类型问题 (window as unknown as { APlayer: typeof APlayer }).APlayer = APlayer; </script>
最后,在 src/components 下新增 APlayer.astro 文件,加入如下代码:
astro--- import 'aplayer/dist/APlayer.min.css'; interface Props { audio: Array<{ name: string; artist: string; url: string; cover: string; lrc: string; [key: string]: any; }>; fixed?: boolean; mini?: boolean; theme?: string; loop?: 'all' | 'one' | 'none'; order?: 'list' | 'random'; volume?: number; } const { audio, fixed = false, mini = false, theme = '#b7daff', loop = 'all', order = 'list', volume = 0.7, } = Astro.props; const instanceId = Math.random().toString(36).substr(2, 9); const config = JSON.stringify({ audio: audio.map(track => ({ ...track, lrc: track.lrc })), fixed, mini, theme, loop, order, volume, autoplay: false }); --- <div id={`aplayer-${instanceId}`} class="aplayer-container" data-config={config}></div> <script is:inline define:vars={{ instanceId, config }}> (function() { let player = null; const initPlayer = () => { const container = document.getElementById(`aplayer-${instanceId}`); if (!container || player) return; const config = JSON.parse(container.dataset.config); player = new APlayer({ container: container, ...config, lrcType: 3 // 使用内置歌词加载功能 }); // 基础事件处理 player.on('error', (error) => { console.error('播放器错误:', error); }); }; const destroyPlayer = () => { if (player) { player.destroy(); player = null; } }; // Astro页面事件监听 document.addEventListener('astro:page-load', initPlayer); document.addEventListener('astro:before-swap', destroyPlayer); // 初始加载 if (document.readyState === 'complete') { initPlayer(); } else { document.addEventListener('DOMContentLoaded', initPlayer); } // 清理播放器 window.addEventListener('beforeunload', destroyPlayer); })(); </script> <style> .aplayer-container { margin: 1rem 0; border-radius: 10px; overflow: hidden; } </style>
现在,应该可以在 mdx 文章中使用前述代码嵌入 APlayer 音乐播放器了。
不过,到这一步为止,我们在文章中嵌入的 APlayer 播放器还不能很好地适配移动端布局。为此,可以在 src/styles/global.css 中加入如下代码:
css/* 移动端优化 */ @media (max-width: 768px) { .aplayer-container { width: 90vw; left: 0rem; padding: 0rem; } }
至此,大功告成。Just enjoy ~
تعليقات