说说 React Router 有几种模式?实现原理?

一、是什么

React Router 提供了多种路由模式,每种模式对应不同的 URL 管理策略和使用场景。路由模式的核心职责是:监听 URL 变化、匹配路由规则、渲染对应组件,同时在导航时更新 URL 而不触发整页刷新。

React Router v6 提供以下几种路由模式:

模式组件URL 示例适用场景
History 模式BrowserRouter/user/123生产环境 Web 应用
Hash 模式HashRouter/#/user/123不支持服务端配置的静态部署
内存模式MemoryRouter无 URL 表现测试环境、React Native
静态模式StaticRouter服务端确定SSR 服务端渲染

二、BrowserRouter — History 模式

基本原理

BrowserRouter 基于 HTML5 History API(pushStatereplaceStatepopstate 事件)实现。它使用真实的 URL 路径(如 /about/users/123),对用户和搜索引擎都更加友好。

History API 核心方法

// pushState:向浏览器历史栈中压入一条新记录,URL 变化但不刷新页面
window.history.pushState(
  { userId: 123 },     // state 对象,可通过 popstate 获取
  '',                   // title(大部分浏览器忽略)
  '/users/123'          // 新的 URL
);

// replaceState:替换当前历史记录,不新增条目
window.history.replaceState(null, '', '/users/456');

// 监听浏览器前进/后退按钮
window.addEventListener('popstate', (event) => {
  console.log('URL 变为:', window.location.pathname);
  console.log('state:', event.state);
});

需要注意:pushStatereplaceState 调用时不会触发 popstate 事件,只有用户点击前进/后退按钮或调用 history.back()/history.forward() 时才会触发。

简化实现原理

import { useState, useEffect, createContext, useContext } from 'react';

interface RouterContextType {
  pathname: string;
  navigate: (to: string, options?: { replace?: boolean }) => void;
}

const RouterContext = createContext<RouterContextType>(null!);

function BrowserRouter({ children }: { children: React.ReactNode }) {
  const [pathname, setPathname] = useState(window.location.pathname);

  useEffect(() => {
    const handlePopState = () => {
      setPathname(window.location.pathname);
    };

    window.addEventListener('popstate', handlePopState);
    return () => window.removeEventListener('popstate', handlePopState);
  }, []);

  const navigate = (to: string, options?: { replace?: boolean }) => {
    if (options?.replace) {
      window.history.replaceState(null, '', to);
    } else {
      window.history.pushState(null, '', to);
    }
    setPathname(to);
  };

  return (
    <RouterContext.Provider value={{ pathname, navigate }}>
      {children}
    </RouterContext.Provider>
  );
}

服务端配置要求

BrowserRouter 需要服务端配合,将所有路径请求都指向 index.html,否则直接访问 /about 会返回 404:

# Nginx 配置
server {
  listen 80;
  root /var/www/app;

  location / {
    try_files $uri $uri/ /index.html;
  }
}

三、HashRouter — Hash 模式

基本原理

HashRouter 利用 URL 中的 hash 部分(# 后面的内容)来管理路由。hash 值的变化不会向服务器发送请求,因此不需要任何服务端配置。

https://example.com/#/users/123

                     hash 部分,不会发送给服务器

hashchange 事件

// 修改 hash
window.location.hash = '#/about';

// 监听 hash 变化
window.addEventListener('hashchange', (event) => {
  console.log('旧 URL:', event.oldURL);
  console.log('新 URL:', event.newURL);
  console.log('当前 hash:', window.location.hash); // "#/about"
});

简化实现原理

function HashRouter({ children }: { children: React.ReactNode }) {
  const [pathname, setPathname] = useState(
    window.location.hash.slice(1) || '/'
  );

  useEffect(() => {
    const handleHashChange = () => {
      const newPath = window.location.hash.slice(1) || '/';
      setPathname(newPath);
    };

    window.addEventListener('hashchange', handleHashChange);
    return () => window.removeEventListener('hashchange', handleHashChange);
  }, []);

  const navigate = (to: string, options?: { replace?: boolean }) => {
    if (options?.replace) {
      const url = new URL(window.location.href);
      url.hash = to;
      window.history.replaceState(null, '', url.toString());
      setPathname(to);
    } else {
      window.location.hash = to;
    }
  };

  return (
    <RouterContext.Provider value={{ pathname, navigate }}>
      {children}
    </RouterContext.Provider>
  );
}

特点

  • 兼容性好,不需要服务端配置
  • URL 中始终带有 #,不够美观
  • hash 值不会出现在 HTTP 请求中,对 SEO 不友好
  • 不支持 state 对象传递(依赖 hash 字符串)

四、MemoryRouter — 内存模式

基本原理

MemoryRouter 将路由状态完全保存在内存中,不与浏览器 URL 产生任何交互。地址栏不会随导航变化,浏览器的前进/后退按钮也不生效。

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

function TestApp() {
  return (
    <MemoryRouter initialEntries={['/dashboard']} initialIndex={0}>
      <App />
    </MemoryRouter>
  );
}

简化实现原理

function MemoryRouter({
  children,
  initialEntries = ['/'],
  initialIndex = 0,
}: {
  children: React.ReactNode;
  initialEntries?: string[];
  initialIndex?: number;
}) {
  const [entries, setEntries] = useState(initialEntries);
  const [index, setIndex] = useState(initialIndex);

  const pathname = entries[index];

  const navigate = (to: string, options?: { replace?: boolean }) => {
    if (options?.replace) {
      setEntries(prev => {
        const next = [...prev];
        next[index] = to;
        return next;
      });
    } else {
      setEntries(prev => [...prev.slice(0, index + 1), to]);
      setIndex(prev => prev + 1);
    }
  };

  return (
    <RouterContext.Provider value={{ pathname, navigate }}>
      {children}
    </RouterContext.Provider>
  );
}

主要用途

  • 单元测试:测试路由相关组件时无需模拟浏览器环境
  • React Native:移动端没有浏览器地址栏
  • 嵌入式应用:作为微前端子应用运行时,不影响宿主 URL

五、StaticRouter — 静态模式

基本原理

StaticRouter 用于服务端渲染(SSR),在服务端根据请求 URL 确定要渲染的组件。它不监听任何事件,也不执行导航操作——服务端只负责单次渲染。

import { StaticRouter } from 'react-router-dom/server';
import { renderToString } from 'react-dom/server';

function handleRequest(req, res) {
  const html = renderToString(
    <StaticRouter location={req.url}>
      <App />
    </StaticRouter>
  );

  res.send(`
    <!DOCTYPE html>
    <html>
      <body>
        <div id="root">${html}</div>
        <script src="/bundle.js"></script>
      </body>
    </html>
  `);
}

SSR 场景下首次渲染使用 StaticRouter,客户端 hydrate 后切换为 BrowserRouter 接管后续路由。

六、模式对比

特性BrowserRouterHashRouterMemoryRouterStaticRouter
URL 格式/path/#/path无 URL 变化由服务端决定
底层 APIHistory APIhashchange内存数组静态传入
服务端配置需要不需要不需要
SEO 支持不适用好(SSR)
浏览器兼容IE 10+IE 8+全部
state 传递支持不支持支持支持
典型场景Web 生产环境静态站点部署测试 / RNSSR

七、路由匹配流程

无论使用哪种路由模式,React Router 内部的匹配流程都是相同的:

1. URL 变化(用户点击链接 / 调用 navigate / 浏览器前进后退)

2. Router 获取最新路径(从 History API / hash / 内存中)

3. 遍历 Route 树,执行路径匹配
   - 精确匹配优先于通配符
   - 动态段 :param 提取参数
   - 嵌套路由递归匹配

4. 构建匹配结果(matches 数组,包含所有层级匹配的路由)

5. 从外向内渲染匹配的组件树(通过 Outlet 串联)

6. v6.4+ Data Router:渲染前先执行 loader 获取数据

八、总结

选择路由模式时的决策路径:

  • 标准 Web 应用BrowserRouter(需要服务端配合 fallback 配置)
  • 纯静态部署 / GitHub PagesHashRouter(无需服务端配置)
  • 单元测试MemoryRouter(可控的初始路由状态)
  • 服务端渲染(SSR)StaticRouter(配合 renderToString 使用)
  • React NativeMemoryRouter(无浏览器环境)

各模式的实现原理核心差异在于 URL 的读写方式不同,但路由匹配和组件渲染的逻辑是完全共享的。理解 History API 和 hashchange 事件是掌握前端路由原理的关键基础。