持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
简介
React-Router6出来有一段时间了,笔者感觉v6版的路由比v4/v5更好用了,不知道你们有没有这样的感觉,特别是在路由嵌套和通过配置文件渲染路由这一块,简直不要太爽。
今天我们对比 React-Router4/5来总结下React-Router6新特性,希望能让小伙伴们快速上手。
<Switch/>替换为<Routes/>
React-Router6版本移除了<Switch/>组件,并使用<Routes/>替换。
除了能替代<Switch/>组件的功能外,也做了一些改变,比如所有的<Route>都必须包裹在<Routes/>中,否则抛出错误。
React-Router4/5 使用 Switch
import { Switch, Route } from "react-router-dom";
<Switch>
<Route path="/home" component={Home} />
<Route path="/login" component={Login} />
</Switch>
React-Router6 使用 Routes
import { Routes, Route } from "react-router-dom";
<Routes>
<Route path="/home" element={<Home/>} />
<Route path="/login" element={<Login/>} />
</Routes>
<Route/>用法变化
React-Router6版本<Route/>组件变化较大,移除了component与render属性,使用element属性替代,因此与之前的版本代码写法不兼容。
React-Router4/5 使用 component 收组件
import { Route } from "react-router-dom";
<Route path="/home" component={Home} />
<Route path="/login" render={()=><Login/>}/>
React-Router6 使用element接收组件
import { Route } from "react-router-dom";
<Route path="/home" element={<Home/>} />
<Route path="/login" element={<Login/>} />
重定向
React-Router6移除了<Redirect/>组件,但可以使用新增的<Navigate/>组件配合<Route/>组件实现重定向效果。
React-Router4/5 使用 Redirect
在React-Router4/5中使用<Redirect/>组件。
import { Route, Redirect } from "react-router-dom";
<Route path='/user'}>
{/* 支持绝对路径和相对路径 */}
<Redirect to="/user/info" />
{/* <Redirect to="info" /> */}
</Route>
React-Router6 使用 Navigate
React-Router6需要<Navigate/>组件配合<Route/>组件实现重定向效果。
不清楚<Navigate/>组件的不要紧,笔者后面会细说。
import { Route, Navigate } from "react-router-dom";
{/* 支持绝对路径和相对路径 */}
<Route path="/user" element={<Navigate to="/user/info"/>}>
<Route path="/user" element={<Navigate to="../info"/>}>
路由跳转
在React-Router4/5中,我们的路由组件props会自动注入history、location、match三个路由相关属性,用来操作路由。
history用来进行路由跳转,location用来进行参数获取,match用来获取params参数。
并且不是路由组件想要使用路由属性也是可以的,类组件可以通过withRouter高阶组件包裹来实现,函数组件可以使用withRouter高阶组件包裹和useHistory、useRouteMatch、useParams三个hook来实现。
但是在React-Router6中,路由组件props不再自动注入history、location、match三个路由相关属性。并且移除了withRouter高阶组件。
React-Router4/5 使用 history、Link、NavLink
React-Router4/5可以使用history或者Link、NavLink来实现路由跳转。
// Link 字符串形式
<Link to="/courses" />
// Link 对象形式
<Link to={{ pathname: "/courses" }} />
// 函数组件 props.history 形式
props.history.push("/courses")
props.history.replace("/courses")
// 函数组件 history hook 形式
const history = useHistory("/courses")
history.push("/courses")
history.replace("/courses")
// 类组件 props.history 形式
this.props.history.push("/courses")
this.props.history.replace("/courses")
React-Router6 使用 navigate、Link、NavLink
因为React-Router6中,路由组件props不再自动注入history所以我们不能再使用以前的方法来进行路由跳转了。
React-Router6新增了navigate用来代替history来进行路由的跳转,但是因为是hook的原因,所以只能用在函数组件中。
那类组件应该怎么办呢?我们可以看看官方的解释。官方也是推荐我们自己来封装withRouter高阶组件,以便类组件能使用路由。
import { useLocation, useNavigate, useParams } from "react-router";
// https://github.com/remix-run/react-router/issues/7256
export default function withRouter(Child) {
return (props) => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams()
return (
<Child {...props} navigate={navigate} location={location} params={params} />
);
};
}
我们用自己封装的withRouter高阶组件包裹我们的类组件就能在类组件props中获取到navigate、location、params对象啦。
后面的例子,笔者假设类组件都使用withRouter包裹了。
React-Router6使用navigate或者Link、NavLink来实现路由跳转。
// Link字符串形式
<Link to="/courses">courses</Link>
// Link对象
<Link to={{ pathname: "/courses"}}>courses</Link>
// 通过自定义withRouter 类组件通过this.props.navigate实现跳转。
// 函数组件通过props.navigate或useNavigate实现跳转。
// navigate 相对路径形式
navigate("../route3/75");
// navigate 绝对路径形式
navigate("/router/route3/75");
// navigate 对象形式
navigate({ pathname: "/courses"});
路由参数传递和参数获取
前面已经说了React-Router6中,路由组件 props 不再自动 注入history、location、match 三个路由相关属性。所以获取参数的方式也发生了改变。
React-Router4/5路由参数传递
React-Router4/5支持params、search、state三种参数。
// Link 字符串形式
<Link to="/courses?sort=name" />
// Link 对象形式
<Link
to={{
pathname: "/courses",
search: "?sort=name",
hash: "#the-hash",
state: { fromDashboard: true }
}}
/>
// 以类组件为例 字符串形式
this.props.history.push("/courses?sort=name")
// 以类组件为例 对象形式
this.props.history.push({
pathname: "/courses",
search: "?sort=name",
hash: "#the-hash",
state: { fromDashboard: true }
})
React-Router4/5路由获取参数
params需要我们配置动态路由,然后在路由的match.params或者在函数组件中使用useParams获取。
search笔者感觉非常不友好,没有vue-router中的query好用。如果要使用需要使用URLSearchParams封装。
state传递参数需要注意的一点是当使用HashRouter的时候,页面刷新state参数会丢失。
// 类组件
this.props.location.search // 获取search参数
this.props.location.state // 获取state参数
this.props.match.params // 获取params
// 函数组件
props.location.search // 获取search参数
props.location.state // 获取state参数
props.match.params // 获取params
// 函数组件 hook形式
const location = useLocation()
const params = useParams()
location.search // 获取search参数
location.state // 获取state参数
params // 获取params
React-Router6 路由传递参数
React-Router6使用navigate来进行路由的跳转和传参。
前面已经说了React-Router6路由组件props不再自动注入history。
在类组件需要我们单独封装withRouter组件才能在类组价获取navigate。
在函数组件我们使用useNavigate获取navigate。
React-Router6传参需要注意两个点:
- 传递
state不再是对象里面传递了,而是单独传递,这是和React-Router4/5的区别。 navigate跳转路径支持相对路径模式,类似我们终端操作。
// Link字符串形式
<Link to="route1?name=randy1#hash1" state={{ fromDashboard: 1 }}>route1</Link>
// Link对象
<Link to={{ pathname: "route3/73",search: "?name=randy2",hash: "#hash2",}} state={{ fromDashboard: 1 }}>route1</Link>
// navigate 相对路径形式
navigate("../route3/75", { state: { fromDashboard: "navigate state" } });
// navigate 绝对路径形式
navigate("/router/route3/75", {
state: { fromDashboard: "navigate state" },
replace: true,
});
React-Router6 路由获取参数
params需要我们配置动态路由,在类组价我们可以使用自己封装的withRouter高阶组件通过params获取。在函数组件中使用useParams获取。
search在类组价我们可以使用自己封装的withRouter高阶组件通过location获取。在函数组件可以使用useSearchParams或useLocation获取。
state在类组价我们可以使用自己封装的withRouter高阶组件通过location获取。在函数组件可以使用useLocation获取。需要注意当使用HashRouter的时候,页面刷新state参数会丢失。
// 类组件
this.props.location.search // 获取search参数
this.props.location.state // 获取state参数
this.props.params // 获取params
// 函数组件
props.location.search // 获取search参数
props.location.state // 获取state参数
props.params // 获取params
// 函数组件 hook形式
const location = useLocation()
const params = useParams()
location.search // 获取search参数
location.state // 获取state参数
params // 获取params
let [searchParams, setSearchParams] = useSearchParams()// 获取search参数
searchParams.get(xxx)
路由嵌套
使用过vue-router的同学是不是感觉react-router的路由嵌套非常难用,至少笔者有这样的感觉。
但是React-Router6版本对路由嵌套这块的改动是比较大的。改版后和vue-router的使用很像。
React-Router4/5 父子都需要定义路由
在React-Router4/5中,子路由配置需要写到对应的子页面,让业务代码和路由逻辑混杂在一起,非常不友好。并且子路由需要补全父路由路径,哇崩溃。
// App.jsx 父组件
<Switch>
<Route path="/home" component={<Home/>} />
<Route path="/user" component={<User/>} />
</Switch>
// User.jsx 子组件
<Switch>
<Route path="/user/info" component={<UserInfo/>} />
<Route path="/user/detail" component={<UserDetail/>} />
</Switch>
React-Router6 支持根组件统一定义路由
他来啦,他来啦。在React-Router6中,子路由配置不强制写到对应的子页面,有新的使用方式,非常友好。并且子路由也无需补全父路由路径。是不是爽到爆。
笔者的感觉就是跟vue-router靠齐了。
在React-Router6中,以前的嵌套写法还是没有移除,只是做了些许改动。
需要注意的有两点:
/user/*,也就是需要在父组件路由下带上*,表示这是一个父路由。- 子路由不再需要补充父路由路径,比如下面的
info、detail,会自动生成/user/info、/user/detail。
// App.jsx 父组件
<Routes>
<Route path="/home" element={<Home/>} />
<Route path="/user/*" element={<User/>} />
</Routes>
// User.jsx 子组件
<Routes>
<Route path="info" element={<UserInfo/>} />
<Route path="detail" element={<UserDetail/>} />
</Routes>
React-Router6全新写法。
- 子路由不再需要写到子路由页面,全部在外面配置。只需要写上
<Outlet />即可,子路由匹配上到时会渲染在这里。类似vue里面的router-view。 - 子路由不再需要补充父路由路径,比如下面的
info、detail,会自动生成/user/info、/user/detail。
// App.jsx 父组件
<Routes>
<Route path="/home" element={<Home/>} />
<Route path="/user" element={<User/>}>
<Route path="info" element={<UserInfo/>} />
<Route path="detail" element={<UserDetail/>} />
</Route>
</Routes>
// User.jsx 子组件
<Outlet />
当然子路由写全路径也是支持的,但是没必要。
// App.jsx 父组件
<Routes>
<Route path="/home" element={<Home/>} />
<Route path="/user" element={<User/>}>
<Route path="/user/info" element={<UserInfo/>} />
<Route path="/user/detail" element={<UserDetail/>} />
</Route>
</Routes>
// User.jsx 子组件
<Outlet />
通过配置渲染路由
在React-Router4/5中,如果想实现类似vue中的routes通过配置来渲染路由是很麻烦的。但在React-Router6使用useRoutes方法可以很方便的实现。
React-Router4/5 使用 map 或 react-router-config
首先我们需要定义routes
// routerConfig/routes.js
import Parent from "./Parent";
import Child1 from "./Child1";
import Child2 from "./Child2";
const routes = [
{
component: Parent,
path: "/parent",
routes: [
{
path: "/parent/child1",
exact: true,
component: Child1,
},
{
path: "/parent/child2",
component: Child2,
},
],
},
];
export default routes;
在根组件,我们需要自己使用map遍历渲染,并把子路由通过routes传递下去。
import routes from "./routerConfig/routes";
<BrowserRouter>
<Switch>
{routes.map((route) => {
return (
<Route
path={route.path}
key={route.path}
render={(props) => {
return (
<route.component
{...props}
routes={route.routes}
></route.component>
);
}}
></Route>
);
})}
</Switch>
</BrowserRouter>
在子组件我们还需要继续使用map遍历渲染。
// Parent.js
<Switch>
{this.props.routes.map((route) => {
return (
<Route
path={route.path}
key={route.path}
render={(props) => {
return (
<route.component
{...props}
routes={route.routes}
></route.component>
);
}}
></Route>
);
})}
</Switch>
是不是很复杂,但是我们可以使用react-router-config插件进行简化。
首先安装react-router-config
npm i react-router-config
在根组件,我们只需要使用renderRoutes方法就可以了。
import { renderRoutes } from "react-router-config";
import routes from "./routerConfig/routes";
// App.jsx
{renderRoutes(routes)}
在子组件我们继续使用renderRoutes。
// Parent.jsx
{renderRoutes(this.props.routes)}
相对自己写遍历是不是方便了很多呢?其实react-router-config插件内部实现跟笔者上面写的差不多。
React-Router6 使用 useRoutes
在React-Router6能更方便的实现。只需要使用useRoutes方法。
首先定义routes
// routerConfig/routes.jsx
import Parent from "./Parent";
import Child1 from "./Child1";
import Child2 from "./Child2";
const routes = [
{
element: <Parent></Parent>,
path: "/parent",
children: [
{
path: "child1",
element: <Child1></Child1>,
},
{
path: "child2",
element: <Child2></Child2>,
},
],
},
];
在根组件,我们只需要使用useRoutes方法就可以了。
这里需要注意useRoutes是hook,所以只能在函数组件或自定义组件使用。
import { useRoutes } from "react-router-dom";
import routes from "./routerConfig/routes";
function App() {
return {useRoutes(routes)}
}
在子组件我们继续使用Outlet渲染子组件就可以了。
// Parent.js
import { Outlet } from "react-router-dom";
<Outlet />
是不是很简单,感觉React路由这一块有点向Vue-Router靠齐了,从路由嵌套和配置式路由就能看出来,后面把路由钩子也更新出来就更爽啦。
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!