手写简版React-router

手写一个简化版本的 React Router,主要实现其核心功能,包括路由配置、路径匹配和导航。

这个简化版本将包括以下组件和函数:

  1. BrowserRouter

  2. Routes 和 Route

  3. Link

  4. useRoutes

  5. 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;
}

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. 补充资料

相关推荐
FungLeo4 小时前
成为全栈·React 管理后台篇·表单页范式:校验、数据回填与未保存保护
react·表单设计·zod·react hook form·成为全栈·数据回填
名字还没想好☜5 小时前
React 文件上传实战:预览 URL 回收、多文件逐个进度与拖拽放置区踩坑
前端·react·next.js
FungLeo8 小时前
成为全栈·React 管理后台篇·列表页范式:让分页、筛选和返回位置进入 URL
react·前端架构·分页查询·react router·成为全栈·url状态
志尊宝1 天前
Vue3 零基础每日笔记(040):Vite 创建 Vue3 + TS 项目——企业标配从这一篇开始
笔记·vue·html·前端开发·软件开发
FungLeo1 天前
成为全栈·React 管理后台篇·服务端状态、会话状态、界面状态:不要都塞进 Zustand
react·状态管理·zustand·react hook form·成为全栈·tanstack query
FungLeo1 天前
成为全栈·React 管理后台篇·前端鉴权闭环:内存令牌、刷新旋转与路由守卫
react·路由守卫·zustand·刷新令牌·成为全栈·前端鉴权
余槐i2 天前
从useState到Agent状态:React状态管理为何在AI工作流中失灵?
react·状态管理·ai agent·langgraph·tool calling
liangshanbo12152 天前
React useTransition 和 useDeferredValue 面试题整理
react·usetransition
梦想的颜色2 天前
【AI科普】AI 时代,纯 H5+CSS PK React & Vue:前端技术孰优孰劣深入剖析
ai·前端框架·大模型·vue·react·html5·vibecoding
liangshanbo12153 天前
面试题:React 中 useReducer 和 useState 有什么区别?什么时候应该使用 useReducer?
react·usereducer