React路由.md 6.4 KB

React路由

React框架配套的路由系统插件 是 React Router。目前版本为V6。

1. 基本使用

  1. 创建路由实例

createBroswerRouter或者createHashRouter

  1. 引入RouterProvider组件

  2. 定义相关路由组件

    // 定义路由的模块 router/index.js
    import { createBrowserRouter } from 'react-router-dom';
    import Home from '../pages/Home';
    import About from '../pages/About';
    
    const routes = [
    {
    path: '/',
    element: <Home />,
    },
    {
    path: '/about',
    element: <About />,
    },
    ];
    
    const router = createBrowserRouter(routes);
    
    export default router;
    
    
    // 入口文件 
    import { RouterProvider } from 'react-router-dom';
    import router from './router';
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
    <React.StrictMode>
    <RouterProvider router={router} />
    </React.StrictMode>
    );
    

2. 嵌套路由

实现分2步走:

  1. 在指定父路由对象上添加children属性,值为路由对象数组;
  2. 在父路由组件上通过Outlet组件来指定子路由组件的渲染位置

    // 定义嵌套子路由
    const routes = [
    {
    path: '/',
    element: <Layout />,
    children: [
      // 这是索引路由
      {
        // path: '',
        index: true,
        element: <Home />,
      },
      {
        path: 'about',
        element: <About />,
      },
    ],
    },
    ];
    
    // 父组件 -- 布局组件
    import Header from '../components/Header';
    import { Outlet } from 'react-router-dom';
    
    function Layout() {
    return (
    <div className="layout">
      <header>
        <Header />
      </header>
      <main style={{ padding: '20px' }}>
        {/* 渲染子路由组件 */}
        <Outlet />
      </main>
    </div>
    );
    }
    
    export default Layout;
    

3. 动态路由

当实现一个类似详情页的需求时,我们可能会需要使用到动态路由

例如,在商品列表页中点击某一商品获取详情。

  1. 假设 商品列表 对应的 路由是 /products
  2. 需要单独页面显示某一商品的详情,此时 路由为这么定义:/products/details/{product-id} 因此这就是动态路由。
  3. 定义路由时,使用":"去定义路由中的参数,即{path: '/products/details/:productId'}
  4. 访问时,会自动匹配。成功后,会将路由参数与值解析出来,存储在params对象中
  5. 通过路由Hook useParams 来获取上面解析后的路由参数对象

    // 列表页代码
    function Products() {
    let [products] = useState(initialProducts);
    return (
    <div className="products">
      <table>
        <thead>
          <tr>
            <th>#</th>
            <th>Name</th>
            <th>Price</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
          {products.map((p, i) => (
            <tr key={p.id}>
              <td>{i + 1}</td>
              <td>{p.name}</td>
              <td>{p.price}</td>
              <td>
                <Link to={{ pathname: `/products/details/${p.id}` }}>详情</Link>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
    );
    }
    
    export default Products;
    // 详情页代码
    import { useParams } from 'react-router-dom';
    
    function ProductDetail() {
    const { productId } = useParams();
    // console.log(params);
    return (
    <div className="detail">
      <h3>详情页</h3>
      <p>您正在浏览的商品ID:{productId}</p>
    </div>
    );
    }
    
    export default ProductDetail;
    

4. 查询参数

在实际开发中,可以通过路由参数实现页面间数据传递;当然也可以通过查询参数。

下面代码演示,如果通过查询参数实现页面间数据传递:

<tbody>
  {products.map((p, i) => (
    <tr key={p.id}>
      <td>{i + 1}</td>
      <td>{p.name}</td>
      <td>{p.price}</td>
      <td>
        {/* 1 路由参数 在页面间传递数据 */}
        {/* <Link to={{ pathname: `/products/details/${p.id}` }}>详情</Link> */}
        {/* 2 查询参数 在页面间传递数据 */}
        {/* <Link to={`/products/detail?pid=${p.id}`}>详情</Link> */}
        <Link to={{ pathname: '/products/detail', search: '?id=1' }}>
          详情
        </Link>
      </td>
    </tr>
  ))}
</tbody>
let [searchParams] = useSearchParams();
<div className="detail">
  {/* <NavLink to="..">Back</NavLink> */}
  <a
    href="#"
    onClick={() => {
      // go('/products');
      // go({ pathname: '/products' });
      go(-1);
    }}
  >
    Back
  </a>
  <h3>详情页</h3>
  <p>
    您正在浏览的商品ID:{searchParams.get('pid')}
  </p>
</div>

5. 页面导航

5.1 声明式导航

  1. Link组件
  2. NavLink组件

NavLink在使用时,更容易实现选中效果。

const handleLinkStyle = ({ isActive, isPending }) =>
  isPending ? 'pending' : isActive ? 'actived' : 'link';

function Header() {
  return (
    <div className="header">
      <ul>
        <li>
          <NavLink className={handleLinkStyle} to="/">
            主页
          </NavLink>
        </li>
        <li>
          <NavLink className={handleLinkStyle} to={{ pathname: '/about' }}>
            关于
          </NavLink>
        </li>
        <li>
          <NavLink className={handleLinkStyle} to={{ pathname: '/products' }}>
            商品
          </NavLink>
        </li>
      </ul>
    </div>
  );
}

5.2 编程式导航

通过 useNavigate()Hook 获取到 navigate函数,接着通过调用navigate函数实现页面跳转。

const navigate = useNavigate();

// navigate(to: Path, option?)
// navigate(delta: number)
navigate(-1)
navigate(1)
navigate('/login')
navigate({pathname: '/login'})
navigate({pathname: '/login', search: '?id=1'})
navigate({pathname: '/login'}, {replace: true, state: {}})

6. 数据获取

在路由系统中,如何选择时机去获取数据?

我们知道在VueRouter中,已经提供了两种方式,都是用户能够接收并且体验良好的方案

  1. 在组件渲染后,添加Loading效果后去获取数据
  2. 在路由开启导航时先去获取数据,这样组件渲染时就会连同数据一起渲染出来了

6.1 Loading效果

6.2 Data Loading

  1. 在定义路由的时候,给路由对象添加loader数据加载器(值为函数),它会在在导航过程中启动数据加载。
  2. 在路由组件中,通过useLoaderData()Hook函数来获取上面loader返回的数据

7. 页面访问权限

8. 其他