React 路由概念解析

Tag: React  Posted on 2020-06-28 18:10:32 Edited on 2020-06-28 19:21:22

HashRouter 与 BrowserRouter 区别

HashRouter:使用 URL 哈希,URL 的形式是:https://domain/path/#/path

BrowserRouter: 没有 #,且需要服务器端进行配置(即对该路径下的所有路径返回同一个页面)。

如果服务端不配置的话,假设我们有两个页面,首页 / 和 /admin,在页面内进行跳转没问题,浏览器不会向服务端进行请求,但是当用户在 /admin 页面刷新页面或者直接在地址栏输入 /admin 进行访问时浏览器会向服务端进行请求,此时将 404 如果服务端没配置的话。为什么前端开发的时候不会出现这种情况?Webpark 可能已经进行过适当的配置,所以才不会 404。所谓的配置即对该子路径下的所有路径都返回同一个页面。

实现原理

  1. 老浏览器的 history: 主要通过 hash 来实现,对应 createHashHistory
  2. 高版本浏览器: 通过 html5 里面的 history,对应 createBrowserHistory
  3. node 环境下: 主要存储在 memeory 里面,对应 createMemoryHistory

从而达到更新 URL 而无需重新加载的效果。

代码示例

我们需要装 react-router-dom,而 react-router 不需要我们手动装。

import { HashRouter, Route, Switch } from 'react-router-dom';

class App extends React.Component {
  render() {
    return (
      <HashRouter>
        <ScrollToTop>
          <Container style={{ marginTop: '5em', minHeight: '80vh' }}>
            <Route path="/" component={Header} />
            <Switch>
              <Route path="/" exact component={Goods} />
              <Route path="/goods" exact component={Goods} />
              <Route path="/goods/new" exact component={EditGoods} />
              <Route path="/goods/:goodsId" exact component={GoodsDetail} />
              <Route path="/goods/edit/:goodsId" exact component={EditGoods} />
              <Route path="/order" exact component={Order} />
              <Route path="/user/:userId" exact component={User} />
              <Route path="/administer" exact component={Administer} />
              <Route path="/login" exact component={Login} />
              <Route path="/register" exact component={Register} />
            </Switch>
          </Container>
          <Footer />
        </ScrollToTop>
      </HashRouter>
    );
  }
}

解读:

注意到,不管对于哪个页面,其都有 Header,为什么呢?因为 Header 对应的路径是 /,每个路径都会匹配 /,这样 Header 就会渲染。

为什么要有 Switch?Switch 内的路由只会渲染第一个匹配的。

为什么要有 exact?为了精确匹配,否则 /goods/2 除了会匹配 /goods/:goodsId 之外也会匹配 /goods。

为什么?因为 /goods 也是 /goods/2 的子串。如果 Swith 内的 /goods 在 /goods/:goodsId 的前面, 当我们访问 /goods/2 前者就会被渲染而 /goods/:goodsId 不会被渲染,这不是我们想要的行为, 因此标明 exact 告诉 React 进行精确匹配。

完整代码示例见:https://github.com/LeeJiangWei/online-mall-client

如何跳转到其他页面

直接使用 a 标签?貌似确实跳转了,但是注意到页面重新加载了!实际上当我们点击后,浏览器向服务器发出了请求。

可不可以不向服务器发出请求呢?

import { Link } from 'react-router-dom';

<Link to="/path"> Go to "/path" </Link>
<Menu.Item as={Link} to="/path"/>

上面两种写法都行,第一种是直接用 Link 组件,第二种的 Menu 是一个 React UI 库中提供的组件。

可不可以直接用代码进行跳转? this.props.history.push('/path');

参考

  1. react-router4入门教程
  2. React 路由中的 Switch 和 exact 的使用