Remix框架下无法在客户端运行自定义JavaScript的问题求助
在Remix中运行客户端JavaScript的正确方式
你遇到的Text content did not match错误,核心原因是Remix基于服务端渲染(SSR)机制:直接在root.tsx中添加内联脚本时,服务端会把脚本内容当成纯文本输出到HTML里,而客户端在水合(hydrate)过程中会对比服务端输出和客户端渲染的内容,内联脚本里的函数定义会导致两边内容不匹配,从而触发报错。
下面是几种可行的解决方案,按推荐程度排序:
方案一:使用React的useEffect钩子(推荐)
这是React/Remix中处理客户端逻辑的标准方式,能和组件生命周期结合,方便清理资源(比如定时器)。
- 创建负责渲染图表的客户端组件:
// app/components/ChartRenderer.tsx import { useEffect } from "react"; export default function ChartRenderer() { useEffect(() => { // 定义渲染函数 const loadData = (divId: string, data: any) => { // Highcharts已全局加载,直接调用即可 Highcharts.stockChart(divId, data); }; // 从服务器获取数据 const fetchData = async () => { // 用相对路径避免跨域问题 const response = await fetch("/getdata/x/y"); const jsonData = await response.json(); loadData("chart-container", jsonData); }; // 页面加载后立即执行一次,之后每5分钟刷新 fetchData(); const intervalId = setInterval(fetchData, 300000); // 组件卸载时清理定时器 return () => clearInterval(intervalId); }, []); // 渲染图表容器 return <div id="chart-container"></div>; }
- 在目标路由组件中引入这个组件:
// app/routes/your-route.tsx import ChartRenderer from "~/components/ChartRenderer"; export default function YourRoute() { return ( <div> <h1>数据图表</h1> <ChartRenderer /> </div> ); }
- 确保在
root.tsx中已加载Highcharts:
// app/root.tsx import { Outlet } from "@remix-run/react"; export default function App() { return ( <html lang="zh-CN"> <head> <script src="https://code.highcharts.com/stock/highstock.js" /> </head> <body> <Outlet /> </body> </html> ); }
方案二:使用外部脚本文件
把自定义JS逻辑写成单独的文件,放在Remix的public目录下,通过外部脚本标签引入,避免SSR解析脚本内容。
- 在
app/public目录下创建chart-script.js文件:
// 渲染数据 function load_data(div_id, data) { Highcharts.stockChart(div_id, data); } // 获取数据 async function get_data() { const response = await fetch("/getdata/x/y"); const json_data = await response.json(); load_data("chart-container", json_data); } // 页面加载后执行,每5分钟刷新 document.addEventListener("DOMContentLoaded", () => { get_data(); setInterval(get_data, 300000); });
- 在
root.tsx中引入这个外部脚本:
// app/root.tsx import { Outlet } from "@remix-run/react"; export default function App() { return ( <html lang="zh-CN"> <head> <script src="https://code.highcharts.com/stock/highstock.js" /> {/* 添加defer确保DOM加载完成后再执行脚本 */} <script src="/chart-script.js" defer /> </head> <body> {/* 确保页面中有对应的容器 */} <div id="chart-container"></div> <Outlet /> </body> </html> ); }
方案三:使用clientOnly组件(适合完全不需要SSR的内容)
如果你的图表完全不需要服务端渲染,可以用Remix的clientOnly组件包裹,确保组件只在客户端渲染:
// app/routes/your-route.tsx import { clientOnly } from "~/utils/client-only"; // 创建只在客户端渲染的组件 const ClientOnlyChart = clientOnly(() => import("~/components/ChartRenderer")); export default function YourRoute() { return ( <div> <h1>数据图表</h1> <ClientOnlyChart /> </div> ); }
clientOnly的实现可以参考Remix官方示例,或直接使用@remix-run/react中的ClientOnly组件(版本支持时)。
内容的提问来源于stack exchange,提问作者Zephyrus
相关产品推荐
相关产品推荐

