npm i react-router
npm i react-router-dom
使用React Router
来定义应用的路由其实比较简单,只需要简单的两个步骤:
index.tsx
文件中声明堆栈路由。<BrowserRouter>
<App />
</BrowserRouter>
function App() {
return (
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
</Routes>
);
}
在react-router-dom
中组件相当于 HTML 中的
标签,我们可以通过它来实现页面组件之间的跳转,我们可以在上述的例子基础上添加一个导航组件,进行页面的跳转。
const Nav: React.FC = () => {
return (
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
</nav>
);
};
function App() {
return (
<>
<Nav />
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
</Routes>
</>
);
}
在react-router-dom
中有一个跟组件类似的导航组件,那就是
组件。这个组件与的不同在于它可以让我们知道哪个菜单组件是出于激活状态,从而让我们可以动态的进行样式设置。具体的例子如下:
return (
<nav>
<NavLink to="/">首页</NavLink>
<NavLink to="/about">关于</NavLink>
</nav>
);
nav a.active {
background: skyblue;
}
上述例子中,我们为当前激活的菜单设置了样式,因为
组件在默认情况下,会在激活状态下的菜单添加一个active
的样式类,我们只要对这个样式类进行样式控制就可以。这个只是简单的操作。我们还可以对当前激活状态下的菜单组件控制如下属性:
className
syle
children
在这里我们只是对className
属性控制进行一个代码举例,具体的代码如下:
function navLinClassName(props: NavLinClassNameProps): string {
return props.isActive ? "active" : "";
}
return (
<nav>
<NavLink to="/" className={navLinClassName}>
首页
</NavLink>
<NavLink to="/about" className={navLinClassName}>
关于
</NavLink>
</nav>
);
在react-router-dom
中提供了一个useNavigate
的钩子函数,此钩子函数主要是让我们实现编程式导航。实现编程式导航的方式有两种:
<button onClick={() => navigate("/order-summary")}>跳转到订单汇总页</button>
<button onClick={() => navigate(-1)}>返回</button>
上述就是两中进行编程式导航的方式,因为react-router
采用的是堆栈路由策略,所以我们在进行导航的时候进行的操作都是进栈和出栈的操作,而当我们有业务需要不让跳转的路由进入到堆栈中时,我们可以使用replace
来控制,具体实例入下:
// order-summary这条路由数据就不会进入到路由堆栈中,这样当使用navigate(-1)时就不会返回这条路由
<button onClick={() => navigate("/order-summary", { replace: true })}>
跳转到订单汇总页
</button>
在react-router
中实现路由嵌套其实很简单,我们只要实现如下几个步骤就可以实现:
Routes
中声明嵌套路由关系<Route path="/products" element={<Products />}>
<Route path="hot" element={<HotProducts />}></Route>
<Route path="baseList" element={<BaseProducts />}></Route>
</Route>
Outlet
声明子路由对应组件展示位置const Products: React.FC = () => {
return (
<>
<input type="text" pattern="搜索产品" />
<nav>
<Link to="/products/hot">热门产品</Link>
<Link to="baseList">产品列表</Link>
</nav>
<Outlet />
</>
);
};
在上述的例子中,我们看到组件的
to
属性设置的值有不同,这里我们需要说明一下,假如跳转的地址带有/
字符的话则是绝对路由,而不带/
字符字符的话则是相对路径。
就比如上述的例子,baseList
是相对于products
这个路径下的子路由,因为 baseList 路由嵌套在 products 下。而/products/hot
采用了绝对路由,所以我们需要在 hot 子路由的基础上添加父组件的路由。
在使用路由嵌套的时候,有一个索引路由的概念,这个索引路由相当于为根级路由设置对应的组件。具体的实例如下:
<Route path="/products" element={<Products />}>
<Route index element={<ProductsIndex />} />
<Route path="hot" element={<HotProducts />}></Route>
<Route path="baseList" element={<BaseProducts />}></Route>
</Route>
通过上述的例子我们可以看到我们在Route
组件中声明了index
属性,这样我们路由在访问products
时,页面就会渲染ProductsIndex
的组件。
有些时候我们需要动态拼接路由,例如我们有一个用户列表,点击对应的用户后会跳转到对应的页面,而我们的路由地址会带上用户的 ID,形成动态路由。具体的代码如下:
<Route path="user" element={<Users />}>
<Route index element={<UserIndex />}></Route>
<Route path="userInfo/:id" element={<UserInfo />}></Route>
</Route>
const UserIndex: React.FC = () => {
const navigate = useNavigate();
return (
<div>
<p onClick={() => navigate("userInfo/1")}>用户1</p>
<p onClick={() => navigate("userInfo/2")}>用户2</p>
<p onClick={() => navigate("userInfo/3")}>用户3</p>
</div>
);
};
const UserInfo: React.FC = () => {
let params = useParams();
return <div>用户ID:{params.id}</div>;
};
为了项目打包能够根据页面路由来拆分打包,从而达到最好的加载速度,我们可以使用懒路由加载来实现,具体的代码如下:
const AblutLayout = React.lazy(() => import('./components/About'))
<Route path='/about' element={
<React.Suspense fallback='Loading....'>
<AblutLayout />
</React.Suspense>
}></Route>