Deviantart
934 字
5 分鐘
移动端目录组件开发记录
功能概述
移动端目录组件是博客的一个辅助功能,它可以在移动设备上显示文章的目录结构,帮助读者快速导航到指定章节。主要特点包括:
- 响应式设计:仅在移动端显示
- 浮动按钮:固定在右下角的快捷入口
- 面板式布局:从底部滑出的目录面板
- 实时更新:页面切换时自动更新目录内容
技术实现
1. 组件结构
使用 Svelte 开发,主要包含以下部分:
- 浮动按钮
- 目录面板
- 背景遮罩
- 目录内容区
基本结构示例:
{#if shouldShow}
<!-- 移动端悬浮按钮 -->
<button class="fixed bottom-20 right-4 z-50">
<!-- 按钮内容 -->
</button>
{#if isOpen}
<div role="dialog" class="fixed inset-0 z-[100]">
<!-- 背景遮罩 -->
<button class="absolute inset-0 bg-black/50"></button>
<!-- 目录面板 -->
<div class="absolute bottom-0 left-0 right-0">
<!-- 面板内容 -->
</div>
</div>
{/if}
{/if}
2. 状态管理
主要的状态变量:
let isOpen = false; // 面板开关状态
let isMobile = false; // 移动端判断
let currentPath = ''; // 当前路径
let isHomePage = false; // 首页判断
let mounted = false; // 组件挂载状态
let currentHeadings = []; // 当前目录内容
let shouldShow = false; // 显示控制
3. 目录更新机制
最初的实现中,我们尝试通过 props 传递目录数据:
export let headings = [];
$: if (mounted && headings) {
currentHeadings = [...headings];
}
但这种方式在页面切换时无法及时更新。最终采用了直接从 DOM 获取标题的方案:
function updateContent() {
if (!mounted) return;
checkPath();
// 获取当前页面的所有标题
const allHeadings = Array.from(document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]'))
.map(heading => ({
depth: parseInt(heading.tagName[1]),
text: heading.textContent || '',
slug: heading.id
}));
currentHeadings = allHeadings;
shouldShow = isMobile && !isHomePage && currentHeadings.length > 0;
}
4. 页面切换处理
为了确保在页面切换时正确更新目录,我们监听了 Astro 的视图转换事件:
function handlePageChange() {
if (!mounted) return;
isOpen = false;
// 等待 DOM 更新完成
setTimeout(() => {
updateContent();
}, 100);
}
onMount(() => {
mounted = true;
checkMobile();
updateContent();
window.addEventListener('resize', checkMobile);
document.addEventListener('astro:page-load', handlePageChange);
document.addEventListener('astro:after-swap', handlePageChange);
return () => {
window.removeEventListener('resize', checkMobile);
document.removeEventListener('astro:page-load', handlePageChange);
document.removeEventListener('astro:after-swap', handlePageChange);
};
});
开发难点
1. 目录更新问题
最初遇到的主要问题是目录内容在页面切换后不会自动更新,需要手动刷新页面。这个问题的解决经历了几个阶段:
- 尝试使用 props 传递数据:无法及时响应页面变化
- 监听 Astro 的页面切换事件:时机不准确
- 最终采用直接从 DOM 获取标题的方案:解决了更新问题
2. 时机把控
在页面切换时,需要确保在正确的时机更新目录内容:
- 太早获取:DOM 还未更新,获取到的是旧内容
- 太晚获取:用户体验不佳
解决方案是使用 setTimeout
配合事件监听:
setTimeout(() => {
updateContent();
}, 100);
3. 性能优化
主要优化点:
- 避免不必要的 DOM 查询
- 控制更新频率
- 优化事件监听器的添加和移除
未来计划
功能增强:
- 添加目录项高亮功能
- 优化动画效果
- 支持更多的自定义选项
技术改进:
- 改进移动端手势操作
- 优化更新机制
- 提供更多自定义选项
其他计划:
- 添加滚动位置同步
- 优化性能
- 提供更多主题选择
总结
移动端目录组件的开发过程让我们深入理解了:
- Astro 的视图转换机制
- Svelte 的响应式更新
- DOM 操作的时机控制
- 移动端交互设计
通过直接获取 DOM 内容的方式,我们最终解决了目录更新的问题,为移动端用户提供了更好的阅读体验。