在路由之间导航时,@esmx/router 会自动管理滚动位置以符合用户预期。推送到新页面时滚动到顶部;返回时恢复之前的滚动位置。这与传统多页面网站的行为一致。
Router 根据导航类型以不同方式处理滚动:
push:滚动到顶部 (0, 0)replace:滚动到顶部 (0, 0)back:恢复保存的滚动位置forward:恢复保存的滚动位置go(n):恢复保存的滚动位置pushWindow:由浏览器处理replaceWindow:由浏览器处理await router.push('/new-page');
await router.back();这开箱即用,无需任何配置。
离开页面时(通过 push、replace 或历史记录导航),Router 会使用两种机制保存当前滚动位置:
Map<string, ScrollPosition>history.state 的 __scroll_position_key 属性中const scrollPosition = { left: window.scrollX, top: window.scrollY };
scrollPositions.set(currentUrl, scrollPosition);
history.replaceState({
...history.state,
__scroll_position_key: scrollPosition
}, '');存储在 history.state 中意味着滚动位置在页面刷新后仍然有效——当用户刷新后再导航返回时,仍然可以恢复正确的滚动位置。
Router 会自动设置 history.scrollRestoration = 'manual'。这告诉浏览器不要尝试自行进行滚动恢复,将完全控制权交给 Router。
这是在导航的 confirm 阶段配置的——你无需自行设置。
有时你不希望导航滚动到顶部。例如,在切换标签页或筛选内容时,用户希望保持在当前位置:
await router.push({
path: '/dashboard',
query: { tab: 'settings' },
keepScrollPosition: true
});当 keepScrollPosition 设置为 true 时:
__keepScrollPosition 标志会存储在 history.state 中在 back/forward 导航期间也会检查此标志——如果目标历史记录条目是使用 keepScrollPosition: true 创建的,则会跳过滚动恢复。
滚动系统支持使用 CSS 选择器滚动到页面上的特定元素。你可以通过 el 属性指定目标:
'#my-id'、'.my-class'、'[data-section]')Element 引用
如果你需要在导航后滚动到某个元素,请使用 afterEach 钩子结合原生滚动 API:
router.afterEach((to) => {
if (to.hash) {
setTimeout(() => {
const el = document.querySelector(to.hash);
if (el) {
el.scrollIntoView({ behavior: 'smooth' });
}
}, 100); // wait for DOM to update
}
});通过层路由打开的路由(使用 pushLayer 或 createLayer)会完全跳过滚动处理。由于层在当前页面之上以覆盖层的形式渲染,滚动背景页面会造成干扰:
await router.pushLayer('/confirm-dialog');此行为内置于 Router 的 confirm 阶段——当 router.isLayer 为 true 时,会跳过滚动逻辑。
以下是不同导航类型中滚动处理的完整流程:
1. 保存当前 URL 的滚动位置
2. 执行导航(更新历史记录,挂载组件)
3. 滚动到 (0, 0) — 除非 keepScrollPosition 为 true1. 保存当前 URL 的滚动位置
2. 执行导航(history popstate 触发)
3. 等待 DOM 更新(nextTick)
4. 检查 history.state 是否有 __keepScrollPosition 标志
→ 如果有:跳过滚动恢复
→ 如果没有:恢复新 URL 的保存滚动位置
→ 如果没有保存的位置则回退到 (0, 0)1. 完整的浏览器导航 — 滚动由浏览器原生处理keepScrollPosition: true 可禁用。'manual')。由 Router 自动设置。history.state。自动完成。