《手写mini React》实现路由功能

53 阅读1分钟
import React, { useState, useEffect, createContext } from 'react';

// 创建一个上下文,用于传递路由信息
const RouterContext = createContext();

// Link 组件,用于创建导航链接
function Link({ to, children }) {
  return (
    // 使用 RouterContext.Consumer 组件包裹,获取路由的历史对象
    <RouterContext.Consumer>
      {({ history }) => (
        // 创建一个 a 标签,设置 href 为传入的 to 属性
        <a
          href={to}
          // 设置点击事件在点击时阻止默认的跳转行为
          onClick={(e) => {
            e.preventDefault();
            // 使用历史对象的 push 方法进行页面跳转
            history.push(to);
          }}
        >
          // 将 children 属性渲染为 a 标签的子元素
          {children}
        </a>
      )}
    </RouterContext.Consumer>
  );
}

// Route 组件,用于渲染对应的页面组件
function Route({ path, component: Component }) {
  return (
    <RouterContext.Consumer>
      {({ currentPath }) => (currentPath === path ? <Component /> : null)}
    </RouterContext.Consumer>
  );
}

// BrowserRouter 组件,用于管理路由状态和导航
function BrowserRouter({ children }) {
  // 使用 state 来存储当前路径
  const [currentPath, setCurrentPath] = useState(window.location.pathname);

  // 监听浏览器前进后退事件,更新路径
  useEffect(() => {
    // 监听浏览器前进后退事件,更新路径
    const handlePopstate = () => {
      // 更新当前路径
      setCurrentPath(window.location.pathname);
    };
    // 添加监听事件
    window.addEventListener('popstate', handlePopstate);
    // 返回一个函数,用于在组件卸载时执行清除操作
    return () => {
      // 移除监听事件
      window.removeEventListener('popstate', handlePopstate);
    };
  // 只监听当前组件的挂载,不依赖任何动态数据,所以这里传入空数组
  }, []);

  // 定义 history 对象,提供 push 方法来导航
  const history = {
    push: (path) => {
      // 使用 pushState 方法来模拟页面导航并更新历史记录,不创建新页面,不加载新资源
      window.history.pushState({}, '', path);
      // 更新当前路径
      setCurrentPath(path);
    },
  };

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

// 使用示例
function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/home">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      <main>
        <Route path="/home" component={Home} />
        <Route path="/about" component={About} />
      </main>
    </BrowserRouter>
  );
}

function Home() {
  return <h2>Home Page</h2>;
}

function About() {
  return <h2>About Page</h2>;
}

export default App;