Angular 11中返回上一页面时如何保持此前滚动到的位置?
Angular 11 路由回退保留滚动位置实现方案
基础结论
该操作不属于浏览器默认行为。SPA单页应用的路由跳转本质是页面局部DOM替换,没有触发完整的页面重载,因此浏览器不会像处理多页应用那样自动保存和恢复滚动位置,需要借助Angular框架能力实现。
实现方案
方案1:使用Angular内置滚动恢复能力(推荐,适配绝大多数场景)
Angular 6及更高版本已经原生支持滚动位置恢复功能,仅需在路由根模块的配置中开启对应参数即可:
// app-routing.module.ts @NgModule({ imports: [RouterModule.forRoot(routes, { // 开启滚动位置恢复 scrollPositionRestoration: 'enabled' })], exports: [RouterModule] }) export class AppRoutingModule { }
开启后效果:
- 点击浏览器回退/前进按钮导航时,自动恢复到对应页面之前的滚动位置
- 点击普通路由链接跳转到新页面时,自动滚动到页面顶部,符合常规使用习惯
如果需要调整顶部偏移(比如页面有固定悬浮头部的场景),可以额外配置scrollOffset参数:
scrollPositionRestoration: 'enabled', // 滚动时距离顶部预留60px的偏移,适配固定头部场景 scrollOffset: [0, 60]
方案2:手动实现滚动位置存储(适配复杂自定义场景)
如果存在嵌套路由、页面内局部滚动容器、异步内容渲染等特殊场景,内置配置无法满足需求时,可以手动实现滚动位置的存储和恢复:
- 首先创建全局服务存储各个路由的滚动位置
// scroll-position.service.ts import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class ScrollPositionService { // 存储格式:路由地址 -> [水平滚动位置, 垂直滚动位置] private positionStore = new Map<string, [number, number]>(); save(routeUrl: string, position: [number, number]): void { this.positionStore.set(routeUrl, position); } get(routeUrl: string): [number, number] | undefined { return this.positionStore.get(routeUrl); } clear(routeUrl?: string): void { routeUrl ? this.positionStore.delete(routeUrl) : this.positionStore.clear(); } }
- 在根组件中监听路由事件,完成滚动位置的保存和恢复
// app.component.ts import { Component } from '@angular/core'; import { NavigationEnd, NavigationStart, Router } from '@angular/router'; import { filter } from 'rxjs/operators'; import { ScrollPositionService } from './scroll-position.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor( private router: Router, private scrollService: ScrollPositionService ) { // 路由开始跳转时,保存当前页面的滚动位置 this.router.events.pipe( filter(event => event instanceof NavigationStart) ).subscribe(() => { this.scrollService.save(this.router.url, [window.scrollX, window.scrollY]); }); // 路由跳转完成后,恢复对应页面的滚动位置 this.router.events.pipe( filter(event => event instanceof NavigationEnd) ).subscribe((event: NavigationEnd) => { const targetPosition = this.scrollService.get(event.urlAfterRedirects); // 等待DOM渲染完成后执行滚动操作 setTimeout(() => { if (targetPosition) { window.scrollTo(targetPosition[0], targetPosition[1]); } else { // 新页面默认滚动到顶部 window.scrollTo(0, 0); } }, 0); }); } }
注意事项
- 如果滚动容器不是全局window,而是页面内的指定DOM元素,只需要将代码中window的滚动属性替换为对应DOM元素的
scrollTop、scrollLeft属性即可 - 如果页面内容为异步加载(比如列表数据从接口获取后渲染),需要将滚动恢复的逻辑延迟到内容渲染完成后执行,避免出现滚动位置不准确的问题
内容的提问来源于stack exchange,提问作者dangchithao
相关产品推荐
相关产品推荐

