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
相关产品推荐
相关产品推荐

