基础路由组件:BrowserRouter与HashRouter的区别

路由是React单页应用的基石。说白了,没有路由,你的应用就只是一堆静态页面。

我个人习惯把路由理解成「导航员」——用户想去哪个页面,路由就负责把对应的组件渲染出来。React Router提供了两种导航员:BrowserRouterHashRouter

它们到底有什么区别?

先看个直观的例子:

// BrowserRouter 的 URL 长这样
https://myapp.com/users/123

// HashRouter 的 URL 长这样
https://myapp.com/#/users/123

看到了吗?HashRouter多了一个 # 号。这个 # 号后面的内容,浏览器不会发送给服务器。所以HashRouter的页面切换,完全靠前端自己处理。

我在项目中遇到过这样一个坑:用BrowserRouter开发时一切正常,一部署到服务器就白屏。为什么?因为用户刷新页面时,浏览器会向服务器请求 /users/123 这个路径,但服务器根本没有这个路由,自然返回404。

⚠️ 注意:BrowserRouter需要服务器配合做URL重定向。所有前端路由都要指向同一个入口HTML文件。

那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 去请求数据
}

你想想看,如果没有动态参数,每个用户都得写一个路由,那得多恐怖。

⚠️ 注意顺序:React Router是按顺序匹配路由的。更具体的路径要放在前面,更通用的放在后面。比如:
// 正确的顺序
<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路由的基本功就算打扎实了。