You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Remix框架下无法在客户端运行自定义JavaScript的问题求助

在Remix中运行客户端JavaScript的正确方式

你遇到的Text content did not match错误,核心原因是Remix基于服务端渲染(SSR)机制:直接在root.tsx中添加内联脚本时,服务端会把脚本内容当成纯文本输出到HTML里,而客户端在水合(hydrate)过程中会对比服务端输出和客户端渲染的内容,内联脚本里的函数定义会导致两边内容不匹配,从而触发报错。

下面是几种可行的解决方案,按推荐程度排序:

方案一:使用React的useEffect钩子(推荐)

这是React/Remix中处理客户端逻辑的标准方式,能和组件生命周期结合,方便清理资源(比如定时器)。

  1. 创建负责渲染图表的客户端组件:
// 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>;
}
  1. 在目标路由组件中引入这个组件:
// app/routes/your-route.tsx
import ChartRenderer from "~/components/ChartRenderer";

export default function YourRoute() {
  return (
    <div>
      <h1>数据图表</h1>
      <ChartRenderer />
    </div>
  );
}
  1. 确保在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解析脚本内容。

  1. 在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);
});
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:43:31