React框架配套的路由系统插件 是 React Router。目前版本为V6。
createBroswerRouter或者createHashRouter
引入RouterProvider组件
定义相关路由组件
// 定义路由的模块 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步走:
在父路由组件上通过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;
当实现一个类似详情页的需求时,我们可能会需要使用到动态路由
例如,在商品列表页中点击某一商品获取详情。
/products
/products/details/{product-id}
因此这就是动态路由。{path: '/products/details/:productId'}
通过路由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;
在实际开发中,可以通过路由参数实现页面间数据传递;当然也可以通过查询参数。
下面代码演示,如果通过查询参数实现页面间数据传递:
<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>
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>
);
}
通过 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: {}})
在路由系统中,如何选择时机去获取数据?
我们知道在VueRouter中,已经提供了两种方式,都是用户能够接收并且体验良好的方案
- 在组件渲染后,添加Loading效果后去获取数据
- 在路由开启导航时先去获取数据,这样组件渲染时就会连同数据一起渲染出来了
useLoaderData()
Hook函数来获取上面loader返回的数据