React (react-router-dom) 单页应用跳转到单独的404页面

3,067 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言:

一个很简单的需求,React 的单页应用,要求站点内 URL不匹配时跳转到单独的404页面。

一开始以为是个很简单就搞定的问题(实际上就是个很简单的问题,只是个人经验不足),结果研究了好几天居然没解决。

特此记录一下思路。

相关代码

效果:

地址栏输入站内不存在的 URL 效果一样。

ezgif-3-e51a6727e402.gif


react-router-dom 官网上的,还是网上搜索到的,大多是下面这种写法。

<Switch>
  <Route exact path="/">
    <Home />
    </Route>
<Route path="/old-match">
  <Redirect to="/will-match" />
    </Route>
<Route path="/will-match">
  <WillMatch />
  </Route>
<Route path="*">
  <NoMatch />
  </Route>
</Switch>

上面代码复制自react-router-dom 官网:

React Router: Declarative Routing for React.js

各个路由对应各自的组件,没有对应组件的路由,path 设置为*,意思为上面路由之外的所有路由,都使用404的组件(上例中是 NoMatch)。

不匹配时显示效果如下:

image.png

这样的404实际上还是在主业务页面里,把业务区域的显示内容置换为404页面的内容,所以还带着菜单。

大多数情况下,想要跳转的是一个单独的404页面,完全和主业务页面独立。

思路如下,嵌套路由,主业务页面和404页面是平级关系,主业务页面里是原来的处理方式,点击菜单显示不同的子页面。

访问不存在的路由时,重定向到404页面。

代码如下:

使用 create-react-app 创建的工程

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
  <BrowserRouter basename="helloreact">
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

App.js

import React from 'react';
import { Route, Switch } from 'react-router';
import Home from './home.jsx';
import './App.css';

function App() {
  return (
    <Switch>
      <Route path="/404" exact>
        <div>404</div>
      </Route>
      <Route path="/">
        <Home></Home>
      </Route>
    </Switch>
  );
}
​
export default App;

App.js 里是父级路由,这里优先匹配404,匹配后显示404页面。 然后需要定义匹配/的路由,匹配时显示的组件为主业务页面。

home.jsx

import { Redirect, Route, Switch, Link } from "react-router-dom";

function Home() {
    return (
        <>
            <ul>
                <ul><Link to="/home">Home</Link></ul>
                <ul><Link to="/about">About</Link></ul>
                <ul><Link to="/nomatch">No Router</Link></ul>
            </ul>

            <Switch>
                <Route path="/" exact>
                    <div>Home</div>
                </Route>
                <Route path="/home" exact>
                    <div>Home</div>
                </Route>
                <Route path="/about" exact>
                    <div>About</div>
                </Route>
                <Redirect path="*" to="/404"></Redirect>
            </Switch>
        </>
    );
}

export default Home;

主页里定义子路由,点击可匹配的路由时,显示区域里(菜单之外)显示对应的组件。

例如,这里点击 Home 时,路由为 /home,就会显示 Home 。

然后当不匹配时,不再使用<Route path="*"> 的写法,而是使用 Redirect 重定向到 404,这样就会在没有匹配的路由时,父级路由匹配404,然后显示一个单独的404页面。