HashRouter 和 BrowserRouter 区别、底层原理、部署差异
HashRouter和BrowserRouter是 React 中最常用的两种路由模式它们最大的区别就是URL 的表现形式、浏览器工作原理以及服务器部署方式。如果你面试 React几乎都会问到这个问题。一、先看区别假设有一个用户页面。HashRouterhttp://localhost:3000/#/userURL 中有#例如http://localhost:3000/#/ http://localhost:3000/#/home http://localhost:3000/#/loginBrowserRouter没有 #http://localhost:3000/user例如http://localhost:3000/ http://localhost:3000/home http://localhost:3000/login更加美观。对比对比项HashRouterBrowserRouterURL/#/home/home是否有 #有无SEO差好刷新页面不会404容易404部署简单需要服务器支持原理hashchangeHistory API推荐后台管理官网、商城、门户二、HashRouter 底层原理HashRouter 利用了浏览器 URL 的#锚点hash。例如https://abc.com/#/home浏览器真正访问服务器的是https://abc.com/后面的#/home不会发送给服务器。浏览器自己保存。例如https://abc.com/#/user?id1服务器收到的是GET /而不是GET /user所以服务器永远不会404。hash 的特点修改 hashlocation.hash /home浏览器不会刷新页面。只会触发window.onhashchange function () { console.log(location.hash); }或者window.addEventListener(hashchange, () { console.log(location.hash); });例如location.hash /homeURLlocalhost:3000/#/home事件触发hashchangeReact Router 就监听这个事件。流程修改hash │ ▼ hashchange │ ▼ React Router │ ▼ 重新匹配Route │ ▼ 重新渲染组件三、BrowserRouter 底层原理BrowserRouter 使用浏览器提供的History API主要就是三个APIhistory.pushState() history.replaceState() window.onpopstate而不是 hash。例如history.pushState({}, , /home);URLlocalhost:3000/home页面不会刷新。React Router 就重新渲染。返回按钮浏览器点击←触发window.onpopstateReact Router 接收到以后重新匹配/home渲染页面。流程pushState │ ▼ history │ ▼ onpopstate │ ▼ React Router │ ▼ 重新渲染四、为什么 BrowserRouter 会404这是最经典的面试题。假设Reactlocalhost:3000/home刷新一下。浏览器发送GET /home服务器收到以后找/home目录但是服务器里面只有index.html没有/home于是404React 根本没有机会运行。HashRouter 为什么不会因为服务器收到的是GET /React 启动以后读取location.hash得到#/home所以正常渲染。五、BrowserRouter 为什么需要服务器配置例如React/home刷新浏览器GET /home服务器应该这样配置任何路径 │ ▼ 都返回 index.htmlReact 接管路由。例如GET /login返回index.htmlReactRoute path/login渲染 Login。所以 SPA 都需要Fallback六、不同服务器如何配置Nginx最经典location / { try_files $uri $uri/ /index.html; }意思先找真实文件/home没有的话index.htmlReact 接管。ApacheRewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^ index.html [L]Expressapp.use(express.static(build)); app.get(*, (req, res) { res.sendFile(path.join(__dirname, build/index.html)); });Nestapp.useStaticAssets(join(__dirname, public)); app.get(*, (req, res) { res.sendFile(join(__dirname, public/index.html)); });Nodeif (!fileExists(req.url)) { res.sendFile(index.html); }七、React Router 内部工作流程假设点击Link to/home流程点击Link │ ▼ preventDefault │ ▼ pushState │ ▼ 更新history │ ▼ 通知Router │ ▼ 重新匹配Route │ ▼ 渲染Home组件整个过程中没有刷新页面。HashRouter点击Link │ ▼ location.hash/home │ ▼ hashchange │ ▼ Router重新匹配 │ ▼ Home组件八、源码思想简化版HashRouterwindow.addEventListener(hashchange, () { render(location.hash); });BrowserRouterwindow.addEventListener(popstate, () { render(location.pathname); });导航function push(path) { history.pushState({}, , path); render(path); }九、History API 详解pushState新增一条历史记录history.pushState({}, , /user);历史记录/ ↓ /home ↓ /user点击返回/homereplaceState替换当前历史记录history.replaceState({}, , /login);历史记录/ ↓ /login不会新增记录。popstate用户← 返回触发window.addEventListener(popstate, () { console.log(location.pathname); });React Router 就是在这里更新页面。十、实际开发如何选择后台管理系统如 OA、CRM、数据平台优先使用HashRouter部署简单不依赖服务器配置在静态托管环境中也能正常工作。企业官网、营销站点、商城、博客等需要更友好 URL 或 SEO 的应用优先使用BrowserRouter但需要配置服务器将未知路径统一回退到index.html。如果你的项目部署在支持自定义路由回退的服务器如 Nginx、Express、Nest 等BrowserRouter通常是更现代、更推荐的选择如果部署环境无法修改服务器配置例如某些静态文件服务器或受限环境HashRouter则是更省心的方案。