博客页面滚动卡顿分析
🔴 核心问题
你的页面在 Edge 浏览器上滚动不流畅,主要有以下几个原因:
1. CSS 动画与过渡冲突(最严重)
问题代码位置
style.css 中的多个 CSS 过渡效果:
/* header-container 有 transition */
.header-container {
transition: height 0.3s ease;
}
/* header 内部元素也有 transition */
header {
transition: padding 0.3s ease;
}
/* placeholder-top 有 transition */
.placeholder-top {
transition: opacity 0.2s ease, transform 0.3s ease;
}
/* 导航按钮有 transition */
nav a {
transition: all var(--transition-normal); /* 0.3s */
}
具体危害
- 层叠多个 transition:当滚动时,header 同时改变
height、padding、opacity、transform - 频繁触发重排/重绘:
height变化导致回流(reflow),opacity导致重绘(repaint) - 与 JS 交互冲突:
script.js中的导航条收缩逻辑每次滚动都添加/移除shrunk类,触发所有这些过渡
性能指标
- 一次完整的回流→重绘 = 掉帧 30-50ms
- 60fps 标准 = 每帧仅有 16.67ms,你已经超标
2. 频繁的 DOM 类名操作
问题代码(script.js 底部)
function updateHeaderState() {
if (!isShrunk && scrollTop > SHRINK_AT) {
header.classList.add('shrunk'); // 触发 CSS transition
isShrunk = true;
lockDuringTransition();
}
}
window.addEventListener('scroll', onScroll, { passive: true });
危害
- 每次滚动都调用
updateHeaderState():即使只是微小滚动也会触发类名变化 - 类名变化 → 所有相关 CSS 选择器重新计算:包括导航按钮、占位符等
requestAnimationFrame虽然有防抖,但执行成本仍然很高
3. Backdrop-filter 性能问题
问题代码
.header-container, .footer-container, .sidebar-container, .article-container {
background-color: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
}
危害
- Backdrop-filter 是最昂贵的 CSS 属性之一
- 在滚动时,GPU 需要持续计算模糊效果
- Edge 对 backdrop-filter 的优化不如 Chrome
4. 过度复杂的 Box-shadow
问题代码
--shadow-card: 0 0 0 11px #f5f5f5, 0 0 0 13px #fff, 0 4px 22px rgba(0,0,0,0.6);
危害
- 三层 box-shadow = 三倍的阴影计算成本
- 滚动时每个可见卡片都在重新计算
- 与 backdrop-filter 结合时,性能成倍下降
5. JavaScript 动画帧不优化
问题代码(代码块折叠功能)
async function initCodeBlocks() {
const blocks = document.querySelectorAll(".article-content pre");
blocks.forEach(pre => {
// 每个 pre 计算字体大小、行高等
const cs = getComputedStyle(codeEl);
const fontSize = parseFloat(cs.fontSize) || 15;
// ... 复杂计算
});
}
危害
getComputedStyle()是同步操作,会阻塞渲染- 在文章很多或代码块很多时,导致主线程卡死
6. 多余的 JavaScript 监听器
问题位置
// 每次滚动都执行这些
document.querySelectorAll('a[href^="#"]').forEach(a => { /* 锚点处理 */ });
document.querySelectorAll('.toggle-replies-btn').forEach(btn => { /* 回复处理 */ });
危害
- 这些操作在
DOMContentLoaded时执行,重复 querySelector 查询整个 DOM - 如果页面有 100+ 个链接,每次都遍历浪费资源
7. 页面加载动画阻塞
问题代码(style.css)
body.page-loaded .header-container,
body.page-loaded .sidebar-container,
body.page-loaded .article-container {
animation: blogEntry 0.52s cubic-bezier(...) forwards;
}
危害
- 页面加载时,所有元素同时动画入场,占用 GPU/CPU
- 即使加载完成后,这些动画类也留在 DOM 中
- 与滚动动画竞争渲染资源
📋 优化方案
方案 A:关键优化(立即见效)
1. 移除 Header 的 CSS Transition
/* 从这个改为 */
.header-container {
/* 删除: transition: height 0.3s ease; */
height: 135px;
min-height: 135px;
}
header {
/* 删除: transition: padding 0.3s ease; */
}
/* 使用 JS 直接改变样式而非 CSS transition */
.placeholder-top {
/* 保留 opacity 但删除 transform transition */
opacity: 0;
transform: translateY(-12px);
/* 使用 JS 控制而非 CSS */
}
2. 简化 Box-shadow
/* 将三层改为一层 */
--shadow-card: 0 4px 12px rgba(0,0,0,0.1);
/* 如果必须要分层效果,用伪元素代替 */
3. 禁用 Backdrop-filter 或降低强度
/* 方案 1:完全移除 */
.header-container, .sidebar-container {
background-color: rgba(255, 255, 255, 0.95);
/* 删除 backdrop-filter */
}
/* 方案 2:仅在需要时启用(hover 时) */
@supports (backdrop-filter: blur(1px)) {
.sidebar-container:hover {
backdrop-filter: blur(3px); /* 降低模糊强度 */
}
}
4. 优化 Header 收缩逻辑
// script.js 底部,改进导航条收缩
(function () {
'use strict';
const header = document.querySelector('.header-container');
if (!header) return;
const SHRINK_AT = 100;
const EXPAND_AT = 40;
let isShrunk = false;
let lastScrollY = 0;
// 使用二次防抖,只在状态真正改变时操作 DOM
function updateHeaderState() {
const scrollY = window.pageYOffset;
// 只检查跨越阈值的情况
if (!isShrunk && scrollY > SHRINK_AT) {
header.classList.add('shrunk');
isShrunk = true;
// 不使用 CSS transition,直接跳变
lastScrollY = scrollY;
} else if (isShrunk && scrollY < EXPAND_AT) {
header.classList.remove('shrunk');
isShrunk = false;
lastScrollY = scrollY;
}
}
// 使用 throttle 而非 requestAnimationFrame
let throttleTimer = null;
window.addEventListener('scroll', () => {
if (throttleTimer) return;
throttleTimer = setTimeout(() => {
updateHeaderState();
throttleTimer = null;
}, 100); // 每 100ms 最多检查一次
}, { passive: true });
updateHeaderState();
})();
5. 延迟非关键代码执行
// script.js 中,将重操作放到空闲时间
document.addEventListener('DOMContentLoaded', function() {
// 关键操作(必须立即执行)
document.body.classList.add('page-loaded');
initAlertAutoHide();
// 非关键操作(延迟执行)
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
initArticleTOC();
initCodeBlocks();
});
} else {
setTimeout(() => {
initArticleTOC();
initCodeBlocks();
}, 1000);
}
});
方案 B:进阶优化
1. 使用 will-change 提示浏览器
.header-container {
will-change: transform; /* 仅在必要时使用 */
}
2. 启用 GPU 加速
.header-container {
transform: translate3d(0, 0, 0); /* 强制 GPU 渲染 */
}
3. 批量修改 DOM
// 不好
element.style.color = 'red';
element.style.fontSize = '16px';
element.style.margin = '10px';
// 更好
element.style.cssText = 'color: red; font-size: 16px; margin: 10px;';
🎯 优化优先级
| 优先级 | 问题 | 预期改进 |
|---|---|---|
| 🔴 高 | 删除 Header CSS Transition | +40% 帧率 |
| 🔴 高 | 简化 Box-shadow | +20% 帧率 |
| 🟠 中 | 禁用/降低 Backdrop-filter | +15% 帧率 |
| 🟠 中 | 优化滚动监听节流 | +10% 帧率 |
| 🟡 低 | 代码块折叠异步化 | +5% 帧率 |
✅ 测试方法
在 Edge 中使用开发者工具验证优化效果:
// 在控制台执行,查看帧率
(function() {
let frameCount = 0;
let lastTime = performance.now();
function countFrames() {
frameCount++;
const now = performance.now();
if (now - lastTime >= 1000) {
console.log(`FPS: ${frameCount}`);
frameCount = 0;
lastTime = now;
}
requestAnimationFrame(countFrames);
}
countFrames();
})();
滚动页面观察控制台输出。目标:维持 50+ FPS(60FPS 是理想值)
总结
你的代码问题主要是:
- CSS 过渡层级过多 ← 最大罪魁祸首
- Backdrop-filter 成本高 ← 次要问题
- Box-shadow 太复杂 ← 边际问题
- JS 防抖不够激进 ← 可优化项
建议先采用方案 A 中的前 4 项,应该能显著改善滚动体验。