需求场景
读者浏览博客时常遇到这种情况:在首页滚动到第 3 屏找到一篇文章,点进去阅读,看完返回首页时,页面回到了顶部,又需要手动滚动到之前的位置。这种体验很割裂,尤其对于内容列表较长的页面。
使用 sessionStorage 记录每个页面的滚动位置,在用户返回时自动恢复,可以实现类似单页应用的无缝浏览体验。
核心实现
创建 scroll-memory.js 模块:
1class ScrollMemory {
2 constructor() {
3 this.throttleDelay = 300;
4 this.lastSaveTime = 0;
5 this.init();
6 }
7
8 init() {
9 this.restore();
10 window.addEventListener('scroll', this.handleScroll.bind(this), {
11 passive: true
12 });
13 }
14
15 handleScroll() {
16 const now = Date.now();
17 if (now - this.lastSaveTime < this.throttleDelay) return;
18 this.lastSaveTime = now;
19 this.save();
20 }
21
22 save() {
23 const key = `scroll:${location.pathname}`;
24 sessionStorage.setItem(key, window.scrollY.toString());
25 }
26
27 restore() {
28 const key = `scroll:${location.pathname}`;
29 const saved = sessionStorage.getItem(key);
30 if (saved !== null) {
31 const targetY = parseInt(saved, 10);
32 window.scrollTo({ top: targetY, behavior: 'instant' });
33 }
34 }
35}
36
37export default ScrollMemory;
关键技术点
被动事件监听
添加滚动监听时传入 { passive: true } 选项。这告诉浏览器该监听器不会调用 preventDefault(),浏览器无需等待 JS 执行完毕即可立即开始滚动,避免滚动卡顿。
节流优化(300ms)
在 handleScroll 中通过时间戳判断距上次保存是否超过 300ms。滚动事件可能以每秒 60 次以上的频率触发,不加节流将导致 sessionStorage.setItem() 被高频调用,引起不必要的性能开销。
按 pathname 索引
使用 scroll:${location.pathname} 作为存储键,确保不同页面(首页、分类页、归档页等)的滚动位置互不干扰。同一页面内不同 URL 参数(如分页)也通过完整的 pathname 自动区分。
使用 sessionStorage 而非 localStorage
sessionStorage 的数据在用户关闭标签页时自动清除,符合「本次浏览会话内保持位置」的需求。如果用 localStorage,位置会持久保留,可能造成用户下次打开站点时被滚动到不明所以的位置。
SWUP 兼容
SWUP 执行页面切换时 DOM 会被替换,需要在内容替换后重新执行恢复逻辑:
1import ScrollMemory from './scroll-memory.js';
2
3document.addEventListener('swup:contentReplaced', () => {
4 new ScrollMemory();
5});
SWUP 的页面切换不会触发完整的页面生命周期,因此需要手动重新初始化滚动记忆模块。但保存在 sessionStorage 中的数据不会因 SWUP 切换而丢失,这意味着:
- 从列表页点击文章 →
sessionStorage保存列表页位置 - 文章页渲染完毕,初始化 ScrollMemory(恢复的是文章页位置,可能为 0)
- 用户按返回或点击导航回列表页 → SWUP 替换内容 → 重新初始化 ScrollMemory → 恢复之前保存的滚动位置
边界处理
sessionStorage不可用时(如隐私模式限制),getItem返回null,代码不做任何处理,自然降级scrollTo使用behavior: 'instant'而非'smooth',避免恢复位置时产生可见的滚动动画- 若页面内容动态加载(如图片懒加载),恢复滚动位置时页面高度可能尚未稳定,可监听页面完全加载后再执行恢复
留言评论
期待你的想法评论加载中