开箱即用,TanStack Router 支持哈希滚动和页面顶部滚动,无需任何额外配置。
默认情况下,滚动到顶部模仿浏览器的行为,这意味着在成功导航后只有 window 本身滚动到顶部。然而,对于许多应用程序来说,由于高级布局,主要可滚动区域通常是嵌套的 div 或类似元素。如果您希望 TanStack Router 也为您滚动这些主要可滚动区域,您可以使用 routerOptions.scrollToTopSelectors 添加选择器来定位它们:
const router = createRouter({
scrollToTopSelectors: ['#main-scrollable-area'],
})
const router = createRouter({
scrollToTopSelectors: ['#main-scrollable-area'],
})
对于无法使用 document.querySelector(selector) 简单解析的复杂选择器,您可以将返回 HTML 元素的函数传递给 routerOptions.scrollToTopSelectors:
const selector = () =>
document
.querySelector('#shadowRootParent')
?.shadowRoot?.querySelector('#main-scrollable-area')
const router = createRouter({
scrollToTopSelectors: [selector],
})
const selector = () =>
document
.querySelector('#shadowRootParent')
?.shadowRoot?.querySelector('#main-scrollable-area')
const router = createRouter({
scrollToTopSelectors: [selector],
})
这些选择器除了 window 之外还会被处理,目前无法禁用 window。
滚动恢复是当用户导航回页面时恢复页面滚动位置的过程。这通常是基于标准 HTML 的网站的内置功能,但对于 SPA 应用程序来说可能难以复制,因为:
不仅如此,应用程序在应用内拥有多个可滚动区域是很常见的,而不仅仅是 body。例如,聊天应用程序可能有一个可滚动的侧边栏和一个可滚动的聊天区域。在这种情况下,您希望独立恢复两个区域的滚动位置。
为了缓解这个问题,TanStack Router 提供了一个滚动恢复组件和钩子,为您处理监控、缓存和恢复滚动位置的过程。
它通过以下方式实现:
这听起来可能很多,但对您来说,就是这么简单:
import { createRouter } from '@tanstack/react-router'
const router = createRouter({
scrollRestoration: true,
})
import { createRouter } from '@tanstack/react-router'
const router = createRouter({
scrollRestoration: true,
})
Note
The <ScrollRestoration /> component still works, but has been deprecated.
遵循 Remix 自己的滚动恢复 API,您还可以使用 getKey 选项自定义用于缓存给定可滚动区域滚动位置的键。例如,这可以用于强制使用相同的滚动位置,无论用户的浏览器历史记录如何。
getKey 选项接收来自 TanStack Router 的相关 Location 状态,并期望您返回一个字符串来唯一标识该状态的可滚动测量值。
默认的 getKey 是 (location) => location.state.__TSR_key!,其中 __TSR_key 是为历史记录中每个条目生成的唯一键。
在 v1.121.34 之前的旧版本使用 state.key 作为默认键,但这已被弃用,取而代之的是 state.__TSR_key。目前,location.state.key 仍然可用于兼容性,但它将在下一个主要版本中被移除。
您可以将滚动同步到路径名:
import { createRouter } from '@tanstack/react-router'
const router = createRouter({
getScrollRestorationKey: (location) => location.pathname,
})
import { createRouter } from '@tanstack/react-router'
const router = createRouter({
getScrollRestorationKey: (location) => location.pathname,
})
您可以有条件地只同步某些路径,然后对其余路径使用键:
import { createRouter } from '@tanstack/react-router'
const router = createRouter({
getScrollRestorationKey: (location) => {
const paths = ['/', '/chat']
return paths.includes(location.pathname)
? location.pathname
: location.state.__TSR_key!
},
})
import { createRouter } from '@tanstack/react-router'
const router = createRouter({
getScrollRestorationKey: (location) => {
const paths = ['/', '/chat']
return paths.includes(location.pathname)
? location.pathname
: location.state.__TSR_key!
},
})
有时您可能希望防止滚动恢复发生。要做到这一点,您可以利用以下 API 上可用的 resetScroll 选项:
当 resetScroll 设置为 false 时,下一次导航的滚动位置将不会被恢复(如果导航到堆栈中现有的历史事件)或重置到顶部(如果它是堆栈中的新历史事件)。
大多数时候,您不需要做任何特殊的事情来使滚动恢复工作。但是,在某些情况下,您可能需要手动控制滚动恢复。最常见的例子是虚拟化列表。
要手动控制整个浏览器窗口内虚拟化列表的滚动恢复:
function Component() {
const scrollEntry = useElementScrollRestoration({
getElement: () => window,
})
// Let's use TanStack Virtual to virtualize some content!
const virtualizer = useWindowVirtualizer({
count: 10000,
estimateSize: () => 100,
// We pass the scrollY from the scroll restoration entry to the virtualizer
// as the initial offset
initialOffset: scrollEntry?.scrollY,
})
return (
<div>
{virtualizer.getVirtualItems().map(item => (
...
))}
</div>
)
}
function Component() {
const scrollEntry = useElementScrollRestoration({
getElement: () => window,
})
// Let's use TanStack Virtual to virtualize some content!
const virtualizer = useWindowVirtualizer({
count: 10000,
estimateSize: () => 100,
// We pass the scrollY from the scroll restoration entry to the virtualizer
// as the initial offset
initialOffset: scrollEntry?.scrollY,
})
return (
<div>
{virtualizer.getVirtualItems().map(item => (
...
))}
</div>
)
}
要手动控制特定元素的滚动恢复,您可以使用 useElementScrollRestoration 钩子和 data-scroll-restoration-id DOM 属性:
function Component() {
// We need a unique ID for manual scroll restoration on a specific element
// It should be as unique as possible for this element across your app
const scrollRestorationId = 'myVirtualizedContent'
// We use that ID to get the scroll entry for this element
const scrollEntry = useElementScrollRestoration({
id: scrollRestorationId,
})
// Let's use TanStack Virtual to virtualize some content!
const virtualizerParentRef = React.useRef<HTMLDivElement>(null)
const virtualizer = useVirtualizer({
count: 10000,
getScrollElement: () => virtualizerParentRef.current,
estimateSize: () => 100,
// We pass the scrollY from the scroll restoration entry to the virtualizer
// as the initial offset
initialOffset: scrollEntry?.scrollY,
})
return (
<div
ref={virtualizerParentRef}
// We pass the scroll restoration ID to the element
// as a custom attribute that will get picked up by the
// scroll restoration watcher
data-scroll-restoration-id={scrollRestorationId}
className="flex-1 border rounded-lg overflow-auto relative"
>
...
</div>
)
}
function Component() {
// We need a unique ID for manual scroll restoration on a specific element
// It should be as unique as possible for this element across your app
const scrollRestorationId = 'myVirtualizedContent'
// We use that ID to get the scroll entry for this element
const scrollEntry = useElementScrollRestoration({
id: scrollRestorationId,
})
// Let's use TanStack Virtual to virtualize some content!
const virtualizerParentRef = React.useRef<HTMLDivElement>(null)
const virtualizer = useVirtualizer({
count: 10000,
getScrollElement: () => virtualizerParentRef.current,
estimateSize: () => 100,
// We pass the scrollY from the scroll restoration entry to the virtualizer
// as the initial offset
initialOffset: scrollEntry?.scrollY,
})
return (
<div
ref={virtualizerParentRef}
// We pass the scroll restoration ID to the element
// as a custom attribute that will get picked up by the
// scroll restoration watcher
data-scroll-restoration-id={scrollRestorationId}
className="flex-1 border rounded-lg overflow-auto relative"
>
...
</div>
)
}
要控制在页面之间导航时的滚动行为,您可以使用 scrollRestorationBehavior 选项。这允许您使页面之间的转换变为即时的,而不是平滑滚动。滚动恢复行为的全局配置具有与浏览器支持的相同选项,即 smooth、instant 和 auto(有关更多信息,请参阅 MDN)。
import { createRouter } from '@tanstack/react-router'
const router = createRouter({
scrollRestorationBehavior: 'instant',
})
import { createRouter } from '@tanstack/react-router'
const router = createRouter({
scrollRestorationBehavior: 'instant',
})