基础路由组件:BrowserRouter与HashRouter的区别
路由是React单页应用的基石。说白了,没有路由,你的应用就只是一堆静态页面。
我个人习惯把路由理解成「导航员」——用户想去哪个页面,路由就负责把对应的组件渲染出来。React Router提供了两种导航员:BrowserRouter和HashRouter。
它们到底有什么区别?
先看个直观的例子:
// BrowserRouter 的 URL 长这样
https://myapp.com/users/123
// HashRouter 的 URL 长这样
https://myapp.com/#/users/123
看到了吗?HashRouter多了一个 # 号。这个 # 号后面的内容,浏览器不会发送给服务器。所以HashRouter的页面切换,完全靠前端自己处理。
我在项目中遇到过这样一个坑:用BrowserRouter开发时一切正常,一部署到服务器就白屏。为什么?因为用户刷新页面时,浏览器会向服务器请求 /users/123 这个路径,但服务器根本没有这个路由,自然返回404。
那HashRouter就没这个问题。因为 # 后面的内容服务器根本看不见,它始终只返回根路径的页面。
| 特性 | BrowserRouter | HashRouter |
|---|---|---|
| URL格式 | 标准路径 | 带 # 号 |
| 服务器配置 | 需要重定向 | 无需配置 |
| SEO友好 | 较好 | 较差 |
| 用户体验 | 更自然 | 带 # 略显怪异 |
我个人的建议是:能上BrowserRouter就上BrowserRouter。除非你的服务器没法配置重定向,或者你只是做个简单的演示项目,才考虑HashRouter。
Route组件的使用
Route 是React Router的核心组件。它的工作很简单:当URL匹配某个路径时,就渲染对应的UI。
基本用法长这样:
import { Route } from 'react-router-dom';
function App() {
return (
<div>
<Route path="/home" component={Home} />
<Route path="/about" component={About} />
</div>
);
}
嗯,这里要注意:Route 本身不包裹任何东西,它只是一个「匹配器」。匹配上了就渲染,没匹配上就什么都不做。
三种渲染方式
React Router给了你三种渲染组件的方式:
// 方式一:component 属性(最常用)
<Route path="/user" component={User} />
// 方式二:render 属性(可以传props)
<Route path="/user" render={(props) => (
<User {...props} extraProp="hello" />
)} />
// 方式三:children 属性(不管是否匹配都渲染)
<Route path="/user" children={(props) => (
<div>{props.match ? '匹配了' : '没匹配'}</div>
)} />
我个人最常用 component 方式,简洁明了。但如果你需要给组件传递额外的props,那就用 render。至于 children,说实话我很少用,它适合做一些特殊的布局控制。
component 时,每次渲染都会创建一个新的组件实例。如果你有性能方面的顾虑,可以考虑用 render 代替。
path与exact属性详解
这两个属性是新手最容易踩坑的地方。我先说结论:exact 就是「精确匹配」的意思。
来看个例子:
<Route path="/" component={Home} />
<Route path="/about" component={About} />
当URL是 /about 时,你觉得会渲染什么?
答案是:两个都渲染。
为什么会这样?因为React Router的匹配规则是「前缀匹配」。 / 是 /about 的前缀,所以 path="/" 也匹配上了。
解决办法就是加 exact:
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
加了 exact 之后,/ 只会在URL完全等于 / 时才渲染。 /about 就不会再触发Home组件了。
我曾经在项目里犯过这个错:首页和列表页同时显示了,排查了半天才发现是 exact 没加。嗯,从那以后我写路由都会习惯性地问自己一句:「这个路径需要精确匹配吗?」
- 根路径
/几乎总是需要exact - 有子路由的父路径,通常不需要
exact - 具体页面路径,加
exact更安全
path的匹配模式
path 属性支持多种匹配模式:
// 静态路径
<Route path="/users" />
// 动态参数(:id 是参数名)
<Route path="/users/:id" />
// 可选参数(? 表示可选)
<Route path="/users/:id?" />
// 通配符(* 匹配任意内容)
<Route path="/files/*" />
动态参数在实际项目中用得特别多。比如用户详情页:
// 路由定义
<Route path="/user/:userId" component={UserDetail} />
// 在组件中获取参数
function UserDetail(props) {
const { userId } = props.match.params;
// 用 userId 去请求数据
}
你想想看,如果没有动态参数,每个用户都得写一个路由,那得多恐怖。
// 正确的顺序
<Route path="/user/create" component={CreateUser} />
<Route path="/user/:id" component={UserDetail} />
// 错误的顺序(:id 会匹配到 "create")
<Route path="/user/:id" component={UserDetail} />
<Route path="/user/create" component={CreateUser} />
好了,基础路由组件就讲到这里。说白了就是三件事:选对Router类型、用好Route组件、搞清楚path和exact的匹配规则。把这些搞明白了,React路由的基本功就算打扎实了。