React-Router 刷新后报错 or Cannot GET /detail

这是今天自己随便鼓捣 React-Router 的官方 demo 所遇到的问题,具体表现是:'/' 根节点 Home 显示无误,不过其他任何路由 例如 /detail,均显示 Cannot GET /detail

在我反复确认 js 代码与官方给出的代码一致后,我开始从 webpack 寻找答案。

果然很快,我就找到类似的问题:

React-router v4 - cannot GET url
React-router urls don’t work when refreshing or writting manually

虽然他们的问题普遍表现在:第一次访问页面是好的,但是在刷新或者手动输入路由地址时,会报 Cannot GET /detail

所幸我照着他们的解决方案也改好了我的 bug

方案1

将 demo 中用的 BrowserRouter 改为 HashRouter 即可

你需要注意的是:

比如你的路由设置为 /detail,原来访问 localhost:8080/detail,在改成 HashRouter 之后,可能需要访问 localhost:8080/#/detail

方案2

修改 webpack.config.js 配置文件

module.exports = {
    // 省略其他的配置
    devServer: {
        historyApiFallback: true
    }
}

devServer 中必须设置 historyApiFallback: true

找寻原因

至于为何会出这个问题呢,这里有一篇文章讲的非常好,我这边做个大概的总结加翻译吧(可能不是很贴切,毕竟英语捉鸡,有兴趣的同学可以直接移步 传送门1 传送门2

首先我们需要知道 React-Router 这类前端路由被称为 client-side routers(后面简称 CSR 吧)。此外,在引入 CSR 后,目前会有两个地方(浏览器、服务端)会进行 URL 解析的工作,在以前浏览器会直接发送一个 GET request 给服务端,服务端做 URL 解析的工作

了解完上面的知识点后,我们先来分析一次正确的 CSR 解析过程。

  1. 浏览器在访问页面(比如首页 "/")之前,客户端还没有加载任何的 js 代码,意味着也没有 React、React Router,所以首次的 GET request 都会被发送到服务端;

  2. 服务端在解析 URL 之后,会返回包含需要的 js 代码的页面,其中就有 React、React Router 等等,首页加载完成

  3. 打个比方,用户点击了首页导航栏中的 About us 按钮,URL 修改成了 http://example.com/about(CSR 会调用 history.pushState 这个浏览器 API 仅在浏览器本地修改了 URL) —— 没有请求发送到服务端没有页面刷新!(因为在第2步后,CSR 已经加载完成,承担了解析 URL 的工作)

所以基本上当你点击了一个跳转按钮,一些 JavaScript 代码运行控制地址栏中的 URL 发生变化(没有页面刷新)。

那么如果手动进行了刷新或是手动输入地址,此时程序会再次绕过 React、React Router 的代码,发送 GET request 到服务端,如果你的服务端没有设置对应的路由可以解析这个 URL,那它就会返回 Cannot GET。尽管相同的 URL 会在客户端是可以正常运行的,那是 React Router 在解析 URL

下面我们来看看上面给的两种解决方案为何可以生效,以及各自的缺陷

Hash Router

Browser Router 改为 Hash Router,页面的 URL 会变为 http://example.com/#/about。在 hash 标志符(#)之后的部分不会被发送到服务端,服务端只能接收到 http://example.com,返回首页以及包含 React、React Router 的 JS 代码。然后 CSR 代码生效,会解析 #/about,并呈现正确的页面。

缺点

  1. 因为 URL 都会自动添加一个 #,不是很好看
  2. 使用此方法将不能使用服务器渲染
historyApiFallback

webpack 手册

进行这项配置是告诉 Webpack Dev Server,当使用 HTML History API(history.pushState) 时,任意的 404 响应都会被重定向到 /index.html

缺点

  1. 只适合开发环境中进行配置

有时候配置了 historyApiFallback 可能会出现下面这样的问题

在这里插入图片描述

这个问题需要配置 webpack.config.js 中的

module.exports = {
    output: {
        publicPath: '/'
    }
}

publicPath 配置的值需要与你的 index.html 引入的 src 对应:

<script type="text/javascript" src="/app.bundle.js"></script>

下面附一份我的 webpack 配置文件,大家可以根据自己的项目情况进行修改

var path = require('path')

module.exports = {
  entry: './src/app.js',
  output: {
    path: path.join(__dirname, './build'),
    filename: 'app.bundle.js',
    publicPath: '/'
  },
  mode: 'development',
  module: {
    rules: [
      { test: /\.js$/, use: 'babel-loader'},
      { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }
    ]
  },
  devServer: {
    historyApiFallback: true
  }
}
若使用 v5 版本的 react-router-dom 出现找不到 useHistory 的错误,可能有以下几个原因。 首先,你需要确认你已经正确地安装了 react-router-dom,并且确保你已经更新到了 v5 版本。你可以使用 `npm list react-router-dom` 命令检查版本号,如果没有正确安装或者版本不正确,你可以使用 `npm install react-router-dom@v5` 命令重新安装。 其次,你需要在代码中正确导入 useHistory。你可以使用以下代码导入 useHistory: ``` import { useHistory } from 'react-router-dom'; ``` 然后,你需要在函数组件中使用 useHistory。useHistory 是一个自定义的 React Hook,用于在函数组件中获取 history 对象。确保你在函数组件的作用域中正确使用它,例如: ``` const YourComponent = () => { const history = useHistory(); // 在这里可以使用 history 对象进行路由导航等操作 ... return ( ... ) } ``` 最后,如果以上方法都尝试过后仍然无法解决问题,可能是其他地方出现了错误导致无法找到 useHistory。你可以检查你的代码中是否存在拼写错误或者其他语法错误,也可以考虑重新启动开发服务器或者清除缓存来刷新代码。 总的来说,如果你正确安装了 v5 版本的 react-router-dom,并正确导入和使用 useHistory,就不应该出现找不到 useHistory 的错误。你可以再仔细检查一下以上的步骤,或者提供更多的错误信息以便我们更好地帮助你解决问题。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值