activated钩子必须用于恢复滚动位置,因为它在keep-alive组件每次激活时触发,此时dom已存在且可见,而mounted仅首次执行;需结合路由路径存储/读取scroll值,并校验有效性后调用scrollto。

在 Vue 单页应用中,使用 activated 钩子恢复滚动位置,核心是“等 DOM 就绪、按路由取值、只对返回生效”。它适用于 <keep-alive></keep-alive> 包裹的组件,确保组件缓存不销毁,从而能复用状态和滚动位置。
为什么必须在 activated 中恢复?
activated 是组件被 <keep-alive></keep-alive> 缓存后重新激活时触发的钩子。此时组件实例未重建,DOM 已存在且即将可见,比 mounted 更准确——mounted 只在首次挂载时执行,而返回时不会再次触发;activated 则每次切回都执行,天然适配“返回即恢复”场景。
恢复前先判断是否为返回行为
避免用户从首页点击进入或刷新页面时误滚动。推荐用浏览器原生 API 判断:
- 检查
performance.getEntriesByType('navigation')[0]?.type === 'back_forward' - 或更轻量方式:在
deactivated中记录时间戳,在activated中对比当前可见性与时间差(如距上次隐藏 document.visibilityState === 'visible') - 若非返回行为,直接跳过恢复逻辑,不读取 sessionStorage
等待真实 DOM 就绪再滚动
不能一进 activated 就 window.scrollTo,否则可能因骨架屏未替换、图片未加载、异步组件未渲染,导致滚动偏移错误。应:
- Vue 2:用
this.$nextTick(() => { /* 滚动 */ }) - Vue 3(Composition API):配合
nextTick或setTimeout(() => {}, 0) - 若页面含懒加载图片,可监听
IntersectionObserver确认关键区域已进入视口;未进入则延迟 300ms 再试,最多重试 2 次
按路由路径读取并应用滚动值
键名必须带路由标识,防止不同页面互相覆盖:
- 存储时用
scroll-${this.$route.path}(如scroll-/list、scroll-/article/123) - 恢复时同样拼接当前路径读取:
const pos = sessionStorage.getItem(`scroll-${this.$route.path}`) - 转为数字后校验有效性:
if (pos && !isNaN(Number(pos))) window.scrollTo(0, Number(pos)) - 注意:不要用固定键名如
scrollPos,否则详情页返回列表页会滚错位置










