持续创作,加速成长!这是我参与「掘金日新计划 · 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
<Switch>
<Route path="/home" component={Home} />
<Route path="/login" component={Login} />
</Switch>
复制代码
React-Router6 使用 Routes
<Routes>
<Route path="/home" element={<Home/>} />
<Route path="/login" element={<Login/>} />
</Routes>
复制代码
<Route/>
用法变化
React-Router6
版本<Route/>
组件变化较大,移除了component
与render
属性,使用element
属性替代,因此与之前的版本代码写法不兼容。
React-Router4/5 使用 component 收组件
<Route path="/home" component={Home} />
<Route path="/login" render={()=><Login/>}/>
复制代码
React-Router6 使用element接收组件
<Route path="/home" element={<Home/>} />
<Route path="/login" element={<Login/>} />
复制代码
重定向
React-Router6
移除了<Redirect/>
组件,但可以使用新增的<Navigate/>
组件配合<Route/>
组件实现重定向效果。
React-Router4/5 使用 Redirect
在React-Router4/5
中使用<Redirect/>
组件。
<Route path='/user'}>
{/* 支持绝对路径和相对路径 */}
<Redirect to="/user/info" />
{/* <Redirect to="info" /> */}
</Route>
复制代码
React-Router6 使用 Navigate
React-Router6
需要<Navigate/>
组件配合<Route/>
组件实现重定向效果。
不清楚<Navigate/>
组件的不要紧,笔者后面会细说。
{/* 支持绝对路径和相对路径 */}
<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" }} />
// 函数组件 history形式
props.history.push("/courses")
props.history.replace("/courses")
// 函数组件 history hook 形式
const history = useHistory("/courses")
history.push("/courses")
history.replace("/courses")
// 类组件 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参数
match.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参数
match.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";
{renderRoutes(routes)}
复制代码
在子组件我们继续使用renderRoutes
。
// Parent.js
{renderRoutes(this.props.routes)}
复制代码
相对自己写遍历是不是方便了很多呢?其实react-router-config
插件内部实现跟笔者上面写的差不多。
React-Router6 使用 useRoutes
在React-Router6
能更方便的实现。只需要使用useRoutes
方法。
首先定义routes
// routerConfig/routes.js
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
靠齐了,从路由嵌套和配置式路由就能看出来,后面把路由钩子也更新出来就更爽啦。
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!