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

React Router v6生产环境Link/navigate失效问题求助

React Router v6.12.1生产环境路由跳转异常排查

问题描述

生产环境使用React Router v6.12.1时,点击<Link>组件或调用navigate()函数后,浏览器URL路径正常变化,但页面完全无响应;刷新页面后,能正常跳转到对应视图。本地开发环境未使用Docker构建镜像直接运行时,不存在此问题。

可能的原因

  • Nginx缺少SPA路由重写配置:生产环境用Nginx托管React静态资源,createBrowserRouter使用HTML5 History模式,访问非根路径时,Nginx会尝试查找对应静态文件,找不到就返回404,导致React Router无法接管路由。必须在Nginx配置中添加规则,把所有请求转发到index.html。
  • 自定义Guard组件未响应路由变化:路由中大量使用的Guard组件如果没监听路由参数/路径变化,或者内部状态未在路由切换时更新,会导致页面不重新渲染。检查Guard是否在路由变化时重新执行权限逻辑,是否正确触发组件更新。
  • Router实例被重复初始化:renderApp函数被KeycloakService.initKeycloak和TranslationService.initTranslations分别调用,可能导致RouterProvider重复渲染,路由内部状态混乱。需确保renderApp只执行一次,或通过状态控制避免重复初始化。
  • 生产构建缓存问题:如果构建后的静态资源没有添加哈希后缀,浏览器可能缓存旧代码,导致路由逻辑未更新。检查React构建配置,确保生成的静态资源文件名包含哈希值,强制浏览器加载最新资源。

相关配置代码

路由配置

export const router = createBrowserRouter([
    {
        path: '/',
        element: <RootPage/>
    },
    {
        path: '/dashboard',
        element: <Guard
                fallback={<DashboardAppShellSkeleton/>}
                uri={'/dashboard/groups'}
                unwantedRoles={['GYM']}><DashboardAppShellComponent/></Guard>,
            children: [
                {
                    path: 'memberships',
                    element: <Guard
                        fallback={<CustomerMembershipsSkeleton/>}
                        uri={'/dashboard'} unwantedRoles={['GYM']}><MyMembershipsPage/></Guard>,
                },
                {
                    path: 'bookings',
                    element: <Guard
                        fallback={<CustomerMembershipsSkeleton/>}
                        uri={'/dashboard'} unwantedRoles={['GYM']}><CustomerBookingsPage/></Guard>,
                },
                {
                    path: 'groups',
                    element: <Guard
                        fallback={<CustomerGroupsSkeleton/>}
                        uri={'/dashboard'} unwantedRoles={['GYM']}><CustomerGroupsPage/></Guard>,
                },
                {
                    path: 'groups/:groupId',
                    element: <Guard
                        fallback={<CustomerSelectedGroupSkeleton/>}
                        uri={'/dashboard'}
                        unwantedRoles={['GYM']}><CustomerSelectedGroupPage/></Guard>,
                },
                {
                    path: "my-profile",
                    element: <Guard
                        fallback={<CustomerMyProfileSkeleton/>}
                        uri={'/dashboard/my-profile'}
                        unwantedRoles={['GYM']}><CustomerMyProfilePage/></Guard>
                }
            ]
        },
        {
            path: '/admin',
            element: <Guard
                fallback={<AdminAppShellSkeleton/>}
                uri={'/admin/members'}
                wantedRoles={['GYM']}><AdminAppShellComponent/></Guard>,
            children: [
                {
                    path: "home",
                    element: <Guard uri={'/admin/home'} wantedRoles={['GYM']}><HomePage/></Guard>
                },
                {
                    path: "members",
                    element: <Guard
                        fallback={<MembersSkeletonPage/>}
                        uri={'/admin/members'}
                        wantedRoles={['GYM']}
                    ><MembersPage/></Guard>,
                },
                {
                    path: "members/:memberId",
                    element: <Guard
                        fallback={<SelectedMemberSkeleton/>}
                        uri={'/admin/members'}
                        wantedRoles={['GYM']}
                    ><SelectedMemberPage/></Guard>
                },
                {
                    path: "memberships",
                    element: <Guard
                        fallback={<MembershipsSkeletonPage/>}
                        uri={'/admin/memberships'}
                        wantedRoles={['GYM']}
                    ><MembershipsPage/></Guard>
                },
                {
                    path: "memberships/:templateId",
                    element: <Guard
                        fallback={<SelectedMembershipSkeletonPage/>}
                        uri={'/admin/memberships'}
                        wantedRoles={['GYM']}
                    ><SelectedMembershipPage/></Guard>
                },
                {
                    path: "classes",
                    element: <Guard
                        fallback={<ClassesSkeletonPage/>}
                        uri={'/admin/classes'}
                        wantedRoles={['GYM']}
                    ><ClassesPage/></Guard>,
                },
                {
                    path: "classes/:classId",
                    element: <Guard
                        fallback={<SelectedClassSkeleton/>}
                        uri={'/admin/classes'}
                        wantedRoles={['GYM']}
                    ><SelectedClassPage/></Guard>
                },
                {
                    path: "classes/:classId/groups/:groupId",
                    element: <Guard
                        fallback={<SelectedGroupSkeleton/>}
                        uri={'/admin/classes'}
                        wantedRoles={['GYM']}
                    ><SelectedGroupPage/></Guard>
                },
                {
                    path: "my-profile",
                    element: <Guard
                        fallback={<MyProfileSkeleton/>}
                        uri={'/admin/my-profile'}
                        wantedRoles={['GYM']}><MyProfilePage/></Guard>
                },
                {
                    path: "statistics",
                    element: <Guard
                        fallback={<StatisticsSkeleton/>}
                        uri={'/admin/my-profile'}
                        wantedRoles={['GYM']}><StatisticsPage/></Guard>
                }

            ]
        },
        {
            path: '/check-in-screen',
            element: <Guard
                fallback={<CheckInScreenSkeleton/>}
                uri={'/admin/members'}
                wantedRoles={['GYM']}
            ><CheckInScreenPage/></Guard>,
        },
        {
            path: '/terms-conditions',
            element: <Suspense fallback={<AdminAppShellSkeleton/>}><TermsConditionsPage/></Suspense>,
        },
        {
            path: '/gym-terms',
            element: <Suspense fallback={<AdminAppShellSkeleton/>}><GymTermsPage/></Suspense>,
        },
        {
            path: '*',
            element: <Suspense fallback={<AdminAppShellSkeleton/>}><NotFoundPage/></Suspense>,
        }
    ]
)

React Router初始化代码

import React from 'react';
import ReactDOM from 'react-dom/client';
import reportWebVitals from './reportWebVitals';
import { ProvidersComponent } from "./features/admin/layout";
import KeycloakService from "./common/utils/auth/keycloak";
import { RouterProvider } from "react-router-dom";
import { router } from "./app.routing";
import HttpService from "./common/utils/rest/HttpService";
import TranslationService from "./i18n";

const root = ReactDOM.createRoot(
    document.getElementById('root') as HTMLElement
);

const renderApp = () => root.render(
    <ProvidersComponent>
        <RouterProvider router={router}/>
    </ProvidersComponent>
);
KeycloakService.initKeycloak(renderApp);
TranslationService.initTranslations(renderApp)
HttpService.configure();

Dockerfile

# Use the latest LTS version of Node.js as the base image
FROM node:18.15.0 as builder

# Set the working directory to /app inside the container
WORKDIR /app

# Copy app files
COPY . .

# Install dependencies (yarn.lock ensures the exact versions of dependencies are installed)
RUN yarn install --frozen-lockfile

# Build the app
RUN yarn run build

# Bundle static assets with nginx
FROM nginx:1.21.0 as production

# Set the environment variable
ENV NODE_ENV local

# Copy built assets from `builder` image
COPY --from=builder /app/build /usr/share/nginx/html

# Add your nginx.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Expose port
EXPOSE 80

# Start nginx
CMD ["nginx", "-g", "daemon off;"]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 20:22:04