React 路由概念解析
HashRouter 与 BrowserRouter 区别
HashRouter:使用 URL 哈希,URL 的形式是:https://domain/path/#/path
BrowserRouter: 没有 #,且需要服务器端进行配置(即对该路径下的所有路径返回同一个页面)。
如果服务端不配置的话,假设我们有两个页面,首页 / 和 /admin,在页面内进行跳转没问题,浏览器不会向服务端进行请求,但是当用户在 /admin 页面刷新页面或者直接在地址栏输入 /admin 进行访问时浏览器会向服务端进行请求,此时将 404 如果服务端没配置的话。为什么前端开发的时候不会出现这种情况?Webpark 可能已经进行过适当的配置,所以才不会 404。所谓的配置即对该子路径下的所有路径都返回同一个页面。
实现原理
- 老浏览器的 history: 主要通过 hash 来实现,对应 createHashHistory
- 高版本浏览器: 通过 html5 里面的 history,对应 createBrowserHistory
- 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');
参考
Links: React-路由概念解析