React Router
React Router是一个用于React应用程序中的路由库,可以帮助用户实现单页应用程序(SPA),同时也可以方便地管理应用程序的URL和视图之间的映射关系。
下面仅对一些面试可能涉及的点进行记录。
React Router包括下面几种路由模式:
-
HashRouter:基于URL中的hash(#)来实现路由,不会向服务器发送请求,适合单页面应用(SPA)。在 URL 中的hash发生变化时,会触发浏览器的hashchange事件,React Router会根据新的hash值来匹配对应的路由。 -
BrowserRouter:基于HTML5的history API实现路由,可以更自然地呈现URL,不需要#。但是需要后端支持,否则在刷新页面时会出现404错误。 -
MemoryRouter:将历史记录存储在内存中,适合测试或其他需要在内存中进行的场景。 -
StaticRouter:用于服务器端渲染,接收一个location和context对象作为props,根据这些参数来决定渲染哪个路由。
比较常用的还是HashRouter和BrowserRouter,我们也需要知道这两种路由模式底层怎么实现的。
HashRouter
Section titled “HashRouter”HashRouter使用URL的hash部分(即#后面的部分)来保持UI和URL的同步。由于hash的变化不会导致浏览器向服务器发送请求,因此HashRouter可以用于不支持HTML5 History API`的旧浏览器或是一些特殊场景。
HashRouter 的工作原理如下:
-
监听
window的hashchange事件,当URL的hash部分发生变化时,更新路由状态并重新渲染相应的组件。 -
使用
window.location.hash来获取和设置hash值。
简化版的实现如下:
class HashRouter { constructor() { this.routes = {}; this.currentUrl = ''; this.init(); }
// 初始化 init() { window.addEventListener('load', this.refresh.bind(this), false); window.addEventListener('hashchange', this.refresh.bind(this), false); }
// 注册路由 route(path, callback) { this.routes[path] = callback || function() {}; }
// 刷新路由 refresh() { this.currentUrl = window.location.hash.slice(1) || '/'; if (this.routes[this.currentUrl]) { this.routes[this.currentUrl](); } }}
// 使用示例const router = new HashRouter();
router.route('/', () => { document.getElementById('content').innerHTML = '这是首页';});
router.route('/about', () => { document.getElementById('content').innerHTML = '这是关于页面';});
router.route('/contact', () => { document.getElementById('content').innerHTML = '这是联系我们页面';});BrowserRouter
Section titled “BrowserRouter”BrowserRouter使用HTML5 History API(pushState、replaceState和popstate事件)来保持UI和URL的同步。它不需要在URL中使用hash,因此URL看起来更自然。但是,它需要服务器配置来支持,因为服务器需要返回同一个index.html页面来处理所有的URL请求。
BrowserRouter 的工作原理如下:
-
使用
history.pushState和history.replaceState来改变URL,而不重新加载页面。 -
监听
window的popstate事件,当用户点击浏览器的前进或后退按钮时,更新路由状态并重新渲染组件。
简化版的实现如下:
class BrowserRouter { constructor() { this.routes = {}; this.init(); }
// 初始化 init() { window.addEventListener('popstate', (e) => { const path = e.state && e.state.path; this.handleRouteChange(path); });
// 初始路由 window.addEventListener('load', () => { this.handleRouteChange(window.location.pathname); }, false); }
// 注册路由 route(path, callback) { this.routes[path] = callback || function() {}; }
// 导航到新路由 navigate(path) { window.history.pushState({ path }, null, path); this.handleRouteChange(path); }
// 处理路由变化 handleRouteChange(path) { const callback = this.routes[path]; if (callback) { callback(); } }}
// 使用示例const router = new BrowserRouter();
router.route('/', () => { document.getElementById('content').innerHTML = '这是首页';});
router.route('/about', () => { document.getElementById('content').innerHTML = '这是关于页面';});
router.route('/contact', () => { document.getElementById('content').innerHTML = '这是联系我们页面';});常用组件和 hooks
Section titled “常用组件和 hooks”下面是React Router比较常用的组件:
-
<BrowserRouter>:用于包裹整个应用程序,并处理路由匹配和导航。 -
<Route>:用于定义路由匹配规则和渲染相应的组件。 -
<Link>:用于在应用程序中创建导航链接,使用户能够浏览到不同的页面。 -
<Switch>:用于包裹多个<Route>组件,只匹配第一个匹配的路由规则。 -
<Redirect>:用于重定向用户到指定的URL。 -
<Params>:用于从URL中提取参数,并将参数传递给路由组件。 -
<Outlet>:用于渲染当前匹配的路由组件。 -
<Navigate>:用于在路由组件之间导航,而无需使用<Link>组件
React Router v6的常用hooks主要有以下几个:
-
useRoutes():用于定义路由规则和路由组件。如:import { useRoutes } from "react-router-dom";const routes = [{ path: "/", element: <Home /> },{ path: "/about", element: <About /> },{path: "/users",element: <Users />,children: [{ path: "/", element: <UsersList /> },{ path: ":id", element: <UserDetails /> },],},];const App = () => {const routing = useRoutes(routes);return <div>{routing}</div>;}; -
useNavigate():用于进行编程式导航。如:import { useNavigate } from "react-router-dom";const Home = () => {const navigate = useNavigate();const handleClick = () => {navigate("/about");};return (<div><h1>Home</h1><button onClick={handleClick}>Go to About</button></div>);}; -
useParams():用于获取路由参数。如:import { useParams } from "react-router-dom";const UserDetails = () => {const { id } = useParams();return (<div><h1>User Details</h1><p>ID: {id}</p></div>);}; -
useLocation():用于获取当前路由的位置信息。如:import { useLocation } from "react-router-dom";const About = () => {const location = useLocation();return (<div><h1>About</h1><p>Current location: {location.pathname}</p></div>);}; -
useMatch():用于获取当前路由的匹配信息。如:import { useMatch } from "react-router-dom";const UsersList = () => {const match = useMatch("/users");return (<div><h1>Users List</h1>{match && <p>Matched path: {match.path}</p>}</div>);};
路由传参方式
Section titled “路由传参方式”1. 动态路由参数(Dynamic Segments)
Section titled “1. 动态路由参数(Dynamic Segments)”在定义路由时,在路径中使用冒号(:)来定义参数。例如:
<Route path="/user/:id" component={User} />在组件中,可以使用 useParams hook 来获取参数。
import { useParams } from 'react-router-dom';function User() { let { id } = useParams(); return <div>User ID: {id}</div>;}2. 查询参数(Query Parameters)
Section titled “2. 查询参数(Query Parameters)”查询参数是URL中问号(?)后面的部分,例如 /user?name=john&age=25。
在React Router中,我们可以使用 useLocation hook 获取到当前的位置对象,然后解析查询字符串。
import { useLocation } from 'react-router-dom';
function User() { const location = useLocation(); const searchParams = new URLSearchParams(location.search); const name = searchParams.get('name'); const age = searchParams.get('age'); return ( <div> Name: {name}, Age: {age} </div> );}3. 状态参数(State)
Section titled “3. 状态参数(State)”状态参数是通过 history.push 或 Link 组件的 state 属性传递的,这些参数不会显示在URL中,而是保存在浏览器的历史记录状态中。
使用 Link 组件传递状态:
import { Link } from 'react-router-dom';// 在组件中<Link to="/user" state={{ from: 'home', id: 123 }}>Go to User</Link>使用 useNavigate(v6)或 history.push(v5)传递状态:
import { useNavigate } from 'react-router-dom';function Home() { const navigate = useNavigate(); const goToUser = () => { navigate('/user', { state: { from: 'home', id: 123 } }); }; return <button onClick={goToUser}>Go to User</button>;}在目标组件中,使用 useLocation 获取传递的状态:
import { useLocation } from 'react-router-dom';function User() { const location = useLocation(); const { from, id } = location.state || {}; return ( <div> From: {from}, ID: {id} </div> );}