在写一个react + express的项目,前端使用了react-router-dom@5.3.4
的BrowserRouter。
/dashboard
这样的,刷新没问题jobEdit/xxx
,从页面点击跳转没问题,但刷新后页面空白。项目结构是这样的:
- client - build // 打包的目录 - index.html - xxx.js- server - app.js
服务端的路由配置如下:
// app.js// 挂载请求路由// ...// 匹配其他路由app.get('*', (req, res) => { res.sendFile( path.resolve(__dirname, '..', 'client', 'build', 'index.html') );});
当访问这个路由/jobEdit/xxx
时,一切正常。然后!我刷新了!发送了这个请求:
它返回了一个html给我。如下图,它居然多了一个jobEdit
文件夹。但我预想是它应该在根目录下返回html给我:
上面的请求很明显发送给服务端了,但/jobEdit/xxx
原本是在前端配置的。我希望它保持在根目录的index.html
中,然后使用前端配置的路由,不需要向服务端发送请求。
// client端的路由 { path: "/jobEdit/:id", key: "jobEdit", component: <JobEdit/> },
错误的后果就是页面空白
查看控制台的错误,服务端发给我的html中引用的js路径出现了问题。
output: { path: path.OUTPUT, filename: '[name].[contenthash].js', clean: true,+ publicPath: '/', },
控制台的文件结构如下,一切正常:
它成功的把返回的index.html的引用路径切到了根目录的js。刷新之后也正常。
但是!会从零开始请求一堆东西:
这跟我的预期还是有很大差异。
我不想要多出来的这个jobEdit
文件夹,于是我想到了下面的重定向方法。
app.get('/', (req, res) => { res.sendFile( path.resolve(__dirname, '..', 'client', 'build', 'index.html') );});app.get('*', (req, res) => { res.redirect('/');});
好的,现在文件结构让人舒服了:
但是它有两个问题:
我在client中代理服务端,登录 -> 跳转到/jobEdit/xxx 都正常,而当我在这个页面重新刷新的时候,就又回到了情况1。其中为了正常运行,webpack进行了如下设置:
devServer: { historyApiFallback: true, },
webpack的配置:
output: { path: path.OUTPUT, // build文件夹 filename: '[name].[contenthash].js', clean: true, },
根目录
import './style.css';import { createRoot } from 'react-dom/client';import App from './App';import Error from './views/Error'import Login from './views/Login';import Register from './views/Register';import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';import { Toaster } from 'react-hot-toast';const root = createRoot(document.getElementById('root'));root.render( <> <Toaster /> <Router> <Switch> <Route path="/login" exact> <Login /> </Route> <Route path="/register" exact> <Register /> </Route> <Route path="/" component={App} /> <Route path='*' component={Error} /> </Switch> </Router> </>);
App.tsx
import { MainRoutes } from '@/routes';import { Header, LeftNav, MainContent } from '@/components/Layout';export default function App() { return ( <div> <Header /> <div className="flex"> <LeftNav /> <MainContent> <MainRoutes /> </MainContent> </div> </div> );}
MainRoutes.tsx
import { Redirect, Route, Switch } from 'react-router-dom';import NotFound from '@/views/NotFound';import PrivateRoute from '@/routes/PrivateRoute';import Dashboard from "@/views/Dashboard";import JobList from '@/views/JobList'import JobAdd from '@/views/JobAdd'import JobEdit from '@/views/JobEdit'import Profile from '@/views/Profile'const MainList = [ { path: "/dashboard", key: "dashboard", title: "Dashboard", component: <Dashboard/> }, { path: "/jobs", key: "jobs", title: "All Jobs", component: <JobList/> }, { path: "/jobAdd", key: "jobAdd", title: "Add Job", component: <JobAdd/> }, { path: "/jobEdit/:id", key: "jobEdit", title: "Update Job", component: <JobEdit/> }, { path: "/profile", key: "profile", title: "Profile", component: <Profile/> },];const MainRoutes = () => { return ( <Switch> <Route exact path="/" key="redirect"> <Redirect to="/dashboard" /> </Route> {MainList.map((item) => ( <PrivateRoute path={item.path} key={item.key}> {item.component} </PrivateRoute> ))} <Route key="not-found"> <NotFound /> </Route> </Switch> );};export default MainRoutes;
PrivateRoute.tsx
const PrivateRoute = ({ children, ...rest }) => { const token = localStorage.getItem('token'); return ( <Route {...rest} render={({ location }) => token ? ( children ) : ( <Redirect to={{ pathname: '/login', state: { from: location }, }} /> ) } /> );};export default PrivateRoute
接口返回授权失败是也进行了跳转设置:
.catch((err) => { if (err.response.status === 401) { toast.error('用户凭证出现问题,请重新登录') location.href = '/login' throw new Error('用户凭证出现问题,请重新登录') } return data })
app.use(express.static(path.resolve(__dirname, '..', 'client', 'build')));// 注册路由const mountRoutes = require('./utils/mouteRouter');mountRoutes(app);app.get('*', (req, res) => { res.sendFile( path.resolve(__dirname, '..', 'client', 'build', 'index.html') );});app.use(notFoundMD); // 匹配其他未定义app.use(errCaptureMiddleware); // 全局错误捕获
mouteRouter.js
const authRoute = require('../routes/auth');const jobsRoute = require('../routes/jobs');const userRoute = require('../routes/user');const authMiddleware = require('../middleware/auth');const apiLimiterMiddleware = require('../middleware/api-limiter');const mountRoutes = (app) => { app.use('/api/v1/auth', apiLimiterMiddleware, authRoute); app.use('/api/v1/jobs', authMiddleware, jobsRoute); app.use('/api/v1/user', authMiddleware, userRoute);};module.exports = mountRoutes;
有什么办法,可以让我在刷新/jobEdit/xxx时,实现一下效果:
我尝试刷新那些正常的路由如/dashboard
,观察控制台:
跟我上面情况1的修改webpack配置的呈现一模一样。(通过webpack配置index.html引用资源的路径,使其指向根目录)
同理,对于client的代理模式,如果使用browserRouter除了需要设置historyApiFallback
,还要从根目录获取其他资源(默认情况下index.html是从同级别的路径获取资源)
module.exports = { devServer: { static: '/', historyApiFallback: true },};
难道这就是最终的解决方案吗?我还是有些孤陋寡闻了
针对您描述的问题,解决方案主要集中在确保服务端能够正确处理前端路由,并且前端能够正确加载资源。以下是一些建议:
确保服务端配置能够正确响应前端路由请求,并返回 index.html
文件。通常,这意味着对所有未找到的资源请求都返回 index.html
。
// 确保 express.static 在所有路由之前,以处理静态文件请求app.use(express.static(path.resolve(__dirname, '..', 'client', 'build')));// 所有其他未找到的路由都应该返回 index.htmlapp.get('/*', (req, res) => { res.sendFile(path.resolve(__dirname, '..', 'client', 'build', 'index.html'));});
注意,这里使用了 /*
而不是 *
。这是因为 *
也会匹配到文件路径(比如 jobEdit/xxx/file.png
),而 /*
则只匹配到路由路径。
确保前端构建时 publicPath
设置为正确的值,以便所有资源引用都是正确的。在 webpack
配置中,您已经设置了 publicPath: '/'
,这是正确的,确保它指向正确的根路径。
当刷新页面时,由于浏览器会向服务器发送请求,您需要确保服务端配置能返回 index.html
。然而,浏览器的 URL 应该保持原样(比如 /jobEdit/xxx
),这样 React Router 可以在客户端接管并渲染正确的组件。
由于您已经设置了服务端返回 index.html
,现在需要确保 React Router 能够根据 URL 渲染正确的组件。这通常在客户端通过 BrowserRouter
和相应的路由配置来完成。
确保在客户端的路由配置中使用了 BrowserRouter
而不是 HashRouter
,因为 BrowserRouter
使用 HTML5 History API 来处理 URL,这允许您使用普通的 URL 路径,而不是带有井号(#
)的哈希路径。
如果每次刷新页面都会重新请求很多资源,那可能是因为缓存控制不当或资源 URL 发生了变化。检查 webpack
配置的 output.filename
,确保它使用了内容哈希([contenthash]
),这样当文件内容没有变化时,URL 就不会变,从而可以利用浏览器缓存。
此外,检查服务端的静态文件缓存头设置,确保合适的缓存策略被应用。
综上所述,您应该:
index.html
。publicPath
。BrowserRouter
来处理客户端路由。通过遵循这些步骤,您应该能够在刷新带有参数的路由时保持页面内容不变,并且不会重新请求整个 index.html
文件。
router配置 点击进去页面时 这样点击后页面会出现但是刷新或者跳转别的路由会报错 刷新报错: 跳转其他路由报错: 如何解决?
问题内容: 我有以下webpack配置文件: 我想要做的就是在本地主机上运行我的应用程序,但是当我打以下命令时:“ http:// localhost:8080 / src / client / home ”(根据我的routes.jsx和运行webpack-dev- server之后) 我懂了 “无法获取/ src / client / home”。 问题答案: 您在路线中提到的第一件事是具有p
问题内容: 我有一个正在使用的应用程序,每当刷新页面时都会遇到问题。我正在使用嵌套视图(命名视图)来构建应用程序。每当我刷新页面时,都不会重新加载当前状态,只会使页面空白。 页面加载等于 我正在通过文件并通过状态循环从文件中读取导航。这就是我可以显示的内容: 对于嵌套json对象中的每个项目,将执行此代码。如果还有其他有用的方法,请告诉我。 问题答案: 有一个问题:AngularJS-UI-rou
我使用的是带有一个简单的路由器插座元件的Angular2 RC5和Angular2/router。我有一个可观察的,在视图中运行良好,但当你刷新页面时(F5或CTRL-F5),它就不起作用了。我在Chrome(最新版本)和IE11(可能还有其他浏览器)中都有这个功能。目前我有一个变通方法,但我不明白为什么我的更改没有自动反映在视图中。 该项目相当大,因此请尽量减少: 在MyService.ts 在
问题内容: 我正在使用Express,它从静态目录加载AngularJS。通常,我会要求Express为其提供我和所有正确的Angular文件等。在我的Angular应用中,我有这些路由设置,它们替换了内容: 在我的主页上,我有到的链接,该链接将成功加载模板并将我定向到或指定的任何ID。问题是当我尝试将浏览器定向到页面或刷新页面时,请求将发送到Express / Node服务器,该服务器返回。 我
本文向大家介绍AngularJS实现页面定时刷新,包括了AngularJS实现页面定时刷新的使用技巧和注意事项,需要的朋友参考一下 有时我们在前端可能会有这样的需求: 1、每隔一段时间刷新一下页面中的数据 2、根据需要可以暂停和启用刷新 接下来我们就来看下AngularJS的实现方法: 首先我们了解到AngularJS中$interval可以用来处理间歇性处理一些事情,那么我们的间歇性刷新就使用i