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

Remix JS服务端环境下localStorage替代方案技术咨询

问题解决:Remix应用复用全局配置数据(避免localStorage服务端报错)

你遇到的ReferenceError: window is not defined是因为Remix的loader函数运行在服务端环境,而localStorage是浏览器专属API,服务端没有window对象,自然会报错。下面给你几种实用的方案实现配置数据一次加载、多页面复用:

方案1:根路由Loader加载+全局共享(推荐)

把配置数据移到根路由的loader中加载,所有子页面都能直接复用,不用重复请求API,这是Remix的官方推荐做法。

// app/root.tsx
import { LoaderFunction } from "@remix-run/node";
import { useLoaderData, Outlet } from "@remix-run/react";
import authenticator from "~/services/auth.server";
import { fetchData } from "~/services/fetch.service";
import Layout from "~/src/Layout";

export let loader: LoaderFunction = async ({ request }) => {
  const user = await authenticator.isAuthenticated(request, { failureRedirect: "/login" });
  
  // 仅在根路由加载一次配置,所有子页面共享
  const configs = await fetchData('GET', request, 'api/configs/all')
    .catch(async () => {
      await authenticator.logout(request, { redirectTo: "/login" });
      return null;
    });

  return { user, configs };
};

export default function Root() {
  const { user, configs } = useLoaderData<typeof loader>();

  // 通过根布局把配置传递给所有子页面,或用React Context全局注入
  return (
    <html>
      <body>
        <Layout user={user} configs={configs}>
          <Outlet />
        </Layout>
      </body>
    </html>
  );
}

之后任何子页面都可以直接从useLoaderData或Context中获取配置,无需重复请求。

方案2:客户端安全使用localStorage缓存

如果一定要用localStorage做客户端缓存,必须确保代码只在浏览器环境执行,比如放在useEffect中:

// 子页面组件
import { useEffect, useState } from "react";
import { useLoaderData } from "@remix-run/react";
import Layout from "~/src/Layout";

export default function DashboardPage() {
  const { user, request } = useLoaderData();
  const [configs, setConfigs] = useState(null);

  useEffect(() => {
    // 这段代码仅在浏览器端运行
    const savedConfigs = localStorage.getItem('parameters');
    if (savedConfigs) {
      setConfigs(JSON.parse(savedConfigs));
    } else {
      // 无缓存时请求一次并存入localStorage
      fetchData('GET', request, 'api/configs/all')
        .then(res => {
          setConfigs(res);
          localStorage.setItem('parameters', JSON.stringify(res));
        });
    }
  }, []);

  return <Layout user={user} configs={configs}></Layout>;
}

⚠️ 注意:绝对不要在loader或其他服务端执行的代码中调用localStorage。

方案3:Session存储用户专属配置

如果配置是和当前用户绑定的,可以存入Remix的Session中,服务端和客户端都能访问:

// 根路由loader
export let loader: LoaderFunction = async ({ request }) => {
  const user = await authenticator.isAuthenticated(request, { failureRedirect: "/login" });
  const session = await authenticator.getSession(request.headers.get("Cookie"));
  
  // 优先从session取配置,无则请求并存入
  let configs = session.get('configs');
  if (!configs) {
    configs = await fetchData('GET', request, 'api/configs/all')
      .catch(async () => {
        await authenticator.logout(request, { redirectTo: "/login" });
        return null;
      });
    session.set('configs', configs);
  }

  return { 
    user, 
    configs,
    // 更新session cookie
    headers: {
      "Set-Cookie": await authenticator.commitSession(session),
    },
  };
};

内容的提问来源于stack exchange,提问作者Alexey

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 01:45:33