技术博客代码折叠功能实现方案与优化 1. 为什么博客需要代码折叠功能作为一个技术博主我经常需要在文章里插入大段代码示例。但直接展示所有代码会让文章显得冗长读者需要不断滚动页面才能看完。特别是当代码超过20行时阅读体验会明显下降。去年我在一篇讲解React状态管理的文章里放了三个完整的组件代码示例。发布后收到读者反馈代码块太长了想快速浏览文章内容时总被代码打断。这让我意识到需要一种更优雅的代码展示方式。代码折叠功能完美解决了这个问题。它允许默认只显示代码摘要如关键部分读者可以点击展开查看完整代码对多个代码块可以独立控制展开/折叠状态保持页面整洁的同时不丢失任何技术细节2. 实现方案选型与技术对比2.1 客户端渲染方案方案A纯CSS实现details[open] summary ~ * { animation: fadeIn 0.5s ease-in-out; } keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }优点零JavaScript依赖浏览器原生支持details标签性能最佳缺点样式定制受限无法保存折叠状态动画效果有限方案BJavaScript增强document.querySelectorAll(.code-block).forEach(block { const toggle block.querySelector(.toggle); toggle.addEventListener(click, () { block.classList.toggle(expanded); localStorage.setItem(code-${block.id}, block.classList.contains(expanded)); }); });优点可保存用户偏好更丰富的交互效果支持复杂条件折叠缺点需要额外JS文件首屏加载可能闪烁2.2 服务端渲染方案对于静态站点生成器如Hugo/Jekyll可以在构建时预处理// Hugo的shortcode实现 {{ if .IsNamedParams }} div classfoldable-code>.code-folder { margin: 1em 0; border-radius: 4px; overflow: hidden; } .code-folder summary { padding: 0.5em 1em; background: #f5f7fa; cursor: pointer; position: relative; font-family: monospace; } .code-folder summary::marker { content: ▶ ; font-size: 0.8em; } .code-folder[open] summary::marker { content: ▼ ; } .code-folder pre { margin-top: 0 !important; border-radius: 0 0 4px 4px !important; }3.2 修改Markdown渲染器编辑/scripts/code-folding.jshexo.extend.filter.register(after_post_render, function(data) { data.content data.content.replace( /precode class([^])([\s\S]*?)\/code\/pre/g, details classcode-foldersummary查看 $1 代码/summaryprecode class$1$2/code/pre/details ); return data; });3.3 添加交互增强在主题的footer.ejs中加入document.querySelectorAll(.code-folder).forEach(details { // 恢复上次状态 const storageKey code-fold-${details.textContent.substr(0, 20)}; const savedState localStorage.getItem(storageKey); if (savedState true) details.open true; // 监听状态变化 details.addEventListener(toggle, () { localStorage.setItem(storageKey, details.open); }); });4. 高级功能实现技巧4.1 多级嵌套折叠details classcode-folder summary外层代码/summary precode.../code/pre details classcode-folder summary内层实现/summary precode.../code/pre /details /details4.2 语言特定样式.code-folder[data-langjavascript] summary { background: #f0db4f20; border-left: 3px solid #f0db4f; } .code-folder[data-langpython] summary { background: #3572a520; border-left: 3px solid #3572a5; }4.3 移动端优化media (max-width: 768px) { .code-folder summary { padding: 0.8em; font-size: 0.9em; } .code-folder pre { max-width: calc(100vw - 2em); overflow-x: auto; } }5. 性能优化与调试5.1 减少重绘// 使用requestAnimationFrame批量处理 let updateQueue []; const processQueue () { updateQueue.forEach(fn fn()); updateQueue []; requestAnimationFrame(processQueue); };5.2 内存管理// 清理过期的localStorage Object.keys(localStorage) .filter(key key.startsWith(code-fold-)) .forEach(key { if (!document.querySelector([data-storage-key${key}])) { localStorage.removeItem(key); } });5.3 常见问题排查问题1代码高亮失效确保折叠逻辑在高亮之后执行检查选择器是否冲突问题2状态保存异常检查storageKey是否唯一验证localStorage是否可用问题3移动端点击不灵敏增加点击区域添加触摸反馈样式6. 替代方案评估6.1 使用Prism.js插件npm install prismjs-fold-code配置示例Prism.plugins.foldCode({ onToggle: function (el, isFolded) {}, foldDelay: 200, foldOnLoad: true });6.2 GitHub风格的折叠.blob-code-context { background-color: #fffbdd; border-color: #e1e4e8; border-style: solid; border-width: 1px 1px 0; }6.3 基于React的实现function FoldableCode({ children, language }) { const [expanded, setExpanded] useState(false); return ( div className{code-block ${language}} button onClick{() setExpanded(!expanded)} {expanded ? 收起 : 展开} {language} 代码 /button {expanded ( precode{children}/code/pre )} /div ); }实际部署建议对于静态博客优先考虑服务端方案动态站点可以使用客户端增强。我的个人博客最终选择了Hexodetails标签的方案在保持简单的同时提供了足够好的用户体验。