
本文介绍在 remix.run 应用中实现平滑、可靠的页面内跳转(锚点滚动)的正确方式,涵盖原生 html 锚点、react 事件处理及 typescript 类型安全实践,避免服务端渲染(ssr)下直接操作 dom 导致的错误。
本文介绍在 remix.run 应用中实现平滑、可靠的页面内跳转(锚点滚动)的正确方式,涵盖原生 html 锚点、react 事件处理及 typescript 类型安全实践,避免服务端渲染(ssr)下直接操作 dom 导致的错误。
在 Remix.run 中,直接调用 document.getElementById(...).scrollIntoView() 会在服务端渲染阶段报错(如 ReferenceError: document is not defined),因为 document 对象仅存在于浏览器环境,而 Remix 默认启用 SSR。因此,任何 DOM 操作必须确保仅在客户端执行。
✅ 推荐方案:优先使用原生语义化锚点链接
最简洁、兼容性最佳且无需 JavaScript 的方式是使用标准 HTML 锚点:
// 在组件中(例如 root.tsx 或某个路由组件)
export default function App() {
return (
<link rel="icon" href="data:image/x-icon;base64,AA"><meta><links></links><nav><a href="#section-1" classname="scroll-link">跳转到第一部分</a>
<a href="#section-2" classname="scroll-link">跳转到第二部分</a>
</nav><main><section id="section-1"><h2>第一部分</h2>
<p>这里是内容区域一...</p>
</section><section id="section-2"><h2>第二部分</h2>
<p>这里是内容区域二...</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/ai/1976" title="ModelScope"><img
src="https://img.php.cn/upload/ai_manual/000/000/000/175679966616295.png" alt="ModelScope" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/ai/1976" title="ModelScope" class="overflowclass">ModelScope</a>
<p class="overflowclass">一个面向AI开发者的开源模型与数据社区,提供模型、数据集和相关工具资源,方便用户探索、使用和开发人工智能应用。</p>
</div>
<a rel="nofollow" href="/ai/1976" title="ModelScope" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
</section></main><outlet></outlet><scripts></scripts>
);
}
浏览器会自动处理 href="#id" 点击行为,并平滑滚动至对应元素(现代浏览器默认启用 scroll-behavior: smooth)。你可在全局 CSS 中增强体验:
/* app/styles/app.css */
html {
scroll-behavior: smooth;
}
⚠️ 若需 JavaScript 控制(如动态 ID、自定义滚动选项)
必须将 DOM 操作延迟至组件挂载后(即客户端),推荐使用 useEffect + useRef 或 useCallback 封装安全函数:
import { useEffect, useCallback } from "react";
// 安全的滚动函数:仅在浏览器环境执行
const useScrollTo = () => {
return useCallback((id: string) => {
if (typeof window === "undefined") return;
const el = document.getElementById(id);
if (el) {
el.scrollIntoView({ behavior: "smooth", block: "start" });
}
}, []);
};
// 在组件中使用
export default function HomePage() {
const scrollTo = useScrollTo();
return (
<div>
<button onclick="{()"> scrollTo("section-1")}>
滚动到第一部分(JS 触发)
</button>
<button onclick="{()"> scrollTo("section-2")}>
滚动到第二部分(JS 触发)
</button>
<section id="section-1">...</section><section id="section-2">...</section>
</div>
);
}
? 注意事项与最佳实践
- 避免在 useEffect 外或服务端调用 document:Remix 的 SSR 构建阶段无 DOM,强制访问将导致构建失败或运行时错误。
- ID 唯一性与合法性:确保目标 id 符合 HTML 规范(非空、不以数字开头、无空格),并全局唯一。
- 无障碍支持:原生 自动具备键盘导航(Tab)、屏幕阅读器语义;若用
- TypeScript 类型提示:可为 scrollTo 函数添加类型约束,例如 id: string & { __brand?: 'valid-id' },配合运行时校验进一步提升健壮性。
综上,对于“跳转到指定 section”的需求,优先采用 + scroll-behavior: smooth —— 它零 JS 依赖、SEO 友好、无障碍合规,且完全规避 SSR 风险。仅当业务逻辑复杂(如条件滚动、动画联动)时,再引入受控的客户端 JS 方案。










