手写一个简化版本的 React Router,主要实现其核心功能,包括路由配置、路径匹配和导航。
这个简化版本将包括以下组件和函数:
-
BrowserRouter
-
Routes 和 Route
-
Link
-
useRoutes
-
useParams
1. 基本框架和组件
1.1. BrowserRouter
BrowserRouter 组件使用 HTML5 History API,并为应用提供路由上下文。
javascript
import React, { useState, useEffect, createContext, useContext } from 'react';
const RouterContext = createContext();
function BrowserRouter({ children }) {
const [location, setLocation] = useState(window.location.pathname);
useEffect(() => {
const handlePopState = () => setLocation(window.location.pathname);
window.addEventListener('popstate', handlePopState);
return () => window.removeEventListener('popstate', handlePopState);
}, []);
const navigate = (to) => {
window.history.pushState({}, '', to);
setLocation(to);
};
return (
<RouterContext.Provider value={{ location, navigate }}>
{children}
</RouterContext.Provider>
);
}
1.2. Routes 和 Route
Routes 组件遍历所有子路由,并匹配当前路径,Route 组件定义路径和对应的组件。
javascript
function Routes({ children }) {
const { location } = useContext(RouterContext);
let element;
React.Children.forEach(children, (child) => {
if (!element && React.isValidElement(child) && matchPath(child.props.path, location)) {
element = child;
}
});
return element;
}
function Route({ path, element }) {
return element;
}
1.3. Link
Link 组件用于导航。
javascript
function Link({ to, children }) {
const { navigate } = useContext(RouterContext);
const handleClick = (event) => {
event.preventDefault();
navigate(to);
};
return <a href={to} onClick={handleClick}>{children}</a>;
}
1.4. useRoutes
useRoutes 是一个 hook,用于在函数组件中使用路由。
javascript
function useRoutes(routes) {
const { location } = useContext(RouterContext);
for (let route of routes) {
if (matchPath(route.path, location)) {
return route.element;
}
}
return null;
}
1.5. useParams
useParams 是一个 hook,用于获取路径参数。
javascript
function useParams() {
const { location } = useContext(RouterContext);
const path = location;
const match = path.match(/\/([^\/]+)\/?/);
return match ? { id: match[1] } : {};
}
2. 路径匹配函数
matchPath 用于检查路径是否匹配。
javascript
function matchPath(pattern, pathname) {
const regex = new RegExp(`^${pattern.replace(/:\w+/g, '([^/]+)')}$`);
return regex.test(pathname);
}
3. 示例应用
基础定义完成,我们在 React 中使用一下这个 router 。
javascript
import React from 'react';
import ReactDOM from 'react-dom';
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function User() {
const { id } = useParams();
return <h2>User ID: {id}</h2>;
}
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/user/1">User 1</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/user/:id" element={<User />} />
</Routes>
</BrowserRouter>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
这个简化版的 React Router,主要实现其核心功能,包括路由配置、路径匹配和导航。省略了很多边缘情况和优化,从本例中可以窥见 react-router 框架整体实现最核心的思路。
4. 补充资料
-
react-router-dom 多类型历史:https://github.com/remix-run/react-router/blob/acc2b94088835d1247bdf3a3f883f74cc3570a0/packages/react-router-dom/index.tsx#L262
-
@tanstack/router:TanStack Router
-
浏览器历史记录协议:History API - Web APIs | MDN