路由
概念
路由是根据浏览器不同的url地址展示不同的页面或内容的一种方案,react-router-dom是一个针对react而设计的路由解决方案,可以友好的解决react component到url之间的同步问题,本文将主要针对react-router-dom v6进行整合
安装
npm install react-router-dom@6
package
react-router在npm上发布了三个包:
react-router:包括了react-router大部分核心功能,包括路由匹配算法和一些核心组件react-router-dom:包括了react-router所有的内容,并且添加了一些核心组件如<BrowerRouter/>、<HashRouter/>等react-router-native:包括了react native所有内容,并添加了一些额外的native特定api,不作讨论
<BrowerRouter/>和<HashRouter/>
| 特性 | <BrowerRouter/> | |
|---|---|---|
| 原理 | history | hash(#符号)后的值 |
| 交互 | 后台接收完整请求 | 后台只接收#前内容,#后由浏览器解析 |
| 传值 | loaction.key`loaction.state` | 不向后台传值 |
路由的使用方法
<BrowserRouter>
使用h5内置history内部堆栈进行导航
import * as React from "react";
import * as ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<BrowserRouter>
{/* The rest of your app goes here */}
</BrowserRouter>,
root
);
<HashRouter>
可以将当前位置暂存到
location.hash中,但是不会发送到服务器
import * as React from "react";
import * as ReactDOM from "react-dom";
import { HashRouter } from "react-router-dom";
ReactDOM.render(
<HashRouter>
{/* The rest of your app goes here */}
</HashRouter>,
root
);
<MemoryRouter>
在某些情况下比如移动端,没有url的情况,只能使用Memory记住,用于非dom环境
import * as React from "react";
import { create } from "react-test-renderer";
import {
MemoryRouter,
Routes,
Route,
} from "react-router-dom";
describe("My app", () => {
it("renders correctly", () => {
let renderer = create(
<MemoryRouter initialEntries={["/users/mjackson"]}>
<Routes>
<Route path="users" element={<Users />}>
<Route path=":id" element={<UserProfile />} />
</Route>
</Routes>
</MemoryRouter>
);
expect(renderer.toJSON()).toMatchSnapshot();
});
});
<Link>
使用
<Link>标签可以让用户通过点击导航到另一个页面,内部是一个<a>标签
import * as React from "react";
import { Link } from "react-router-dom";
function UsersIndexPage({ users }) {
return (
<div>
<h1>Users</h1>
<ul>
{users.map((user) => (
<li key={user.id}>
<Link to={user.id}>{user.name}</Link>
</li>
))}
</ul>
</div>
);
}
<NavLink>
和
<Link/>相比唯一区别在于<NavLink>知道其是否被激活,在其激活后会将activeClassName自动添加到标签上
import * as React from "react";
import { NavLink } from "react-router-dom";
function NavList() {
// This styling will be applied to a <NavLink> when the
// route that it links to is currently selected.
let activeStyle = {
textDecoration: "underline"
};
let activeClassName = "underline"
return (
<nav>
<ul>
<li>
<NavLink
to="messages"
style={({ isActive }) =>
isActive ? activeStyle : undefined
}
>
Messages
</NavLink>
</li>
<li>
<NavLink
to="tasks"
className={({ isActive }) =>
isActive ? activeClassName : undefined
}
>
Tasks
</NavLink>
</li>
<li>
<NavLink
to="tasks"
>
{({ isActive }) => (
<span className={isActive ? activeClassName : undefined}>
Tasks
</span>
))}
</NavLink>
</li>
</ul>
</nav>
);
}
<Navigate>
v6中新的重定向组件
import * as React from "react";
import { Navigate } from "react-router-dom";
class LoginForm extends React.Component {
state = { user: null, error: null };
async handleSubmit(event) {
event.preventDefault();
try {
let user = await login(event.target);
this.setState({ user });
} catch (error) {
this.setState({ error });
}
}
render() {
let { user, error } = this.state;
return (
<div>
{error && <p>{error.message}</p>}
{user && (
<Navigate to="/dashboard" replace={true} />
{/*当设置为 true 时,点击链接后将替换历史堆栈中的当前条目,而不是添加新条目。默认为 false。*/}
)}
<form
onSubmit={(event) => this.handleSubmit(event)}
>
<input type="text" name="username" />
<input type="password" name="password" />
</form>
</div>
);
}
}
<Outlet>
新的
this.props.children,在父组件中使用从而展示出其子路由,并且允许在渲染子路由时嵌套UI
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
{/* This element will render either <DashboardMessages> when the URL is
"/messages", <DashboardTasks> at "/tasks", or null if it is "/"
*/}
<Outlet />
</div>
);
}
function App() {
return (
<Routes>
<Route path="/" element={<Dashboard />}>
<Route
path="messages"
element={<DashboardMessages />}
/>
<Route path="tasks" element={<DashboardTasks />} />
</Route>
</Routes>
);
}
<Routes>和<Route>
v6版本中
<Switch/>被重新更名为Routers,但是大部分功能不变,并且现在一个页面允许多个Routes
<Routes>
<Route path="/" element={<Dashboard />}>
<Route
path="messages"
element={<DashboardMessages />}
/>
<Route path="tasks" element={<DashboardTasks />} />
</Route>
<Route path="about" element={<AboutPage />} />
</Routes>
route中
component和render被element替代
useHref
该
useHref钩子返回一个 URL,该 URL 可用于链接到给定to位置,甚至在 React Router 之外。
useLocation
返回当前
location对象。
import * as React from 'react';
import { useLocation } from 'react-router-dom';
function App() {
let location = useLocation();
React.useEffect(() => {
ga('send', 'pageview');
}, [location]);
return (
// ...
);
}
useNavigate
返回一个函数,允许您以
useNavigate编程方式导航,例如在提交表单之后。
import { useNavigate } from "react-router-dom";
function SignupForm() {
let navigate = useNavigate();
async function handleSubmit(event) {
event.preventDefault();
await submitForm(event.target);
navigate("../success", { replace: true });
}
return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}
实际上是对v5
useHistory的改动,navigate()替代了history.push(),并且支持传递增量,如:前进:
navigate(1)后退:
navigate(-1)
useRoutes
等同于
<Routes/>
import * as React from "react";
import { useRoutes } from "react-router-dom";
function App() {
let element = useRoutes([
{
path: "/",
element: <Dashboard />,
children: [
{
path: "messages",
element: <DashboardMessages />,
},
{ path: "tasks", element: <DashboardTasks /> },
],
},
{ path: "team", element: <AboutPage /> },
]);
return element;
}
路由跳转方式
声明式
<NavLink to="/films" activeClassName="active">films</NavLink>
<NavLink to="/cinemas" activeClassName="active">cinemas</NavLink>
<NavLink to="/center" activeClassName="active">center</NavLink>
编程式导航
//v5
this.props.history.push(`/center`)
useHistory()
//v6
useNavigate();
路由传参
v5
| 方法名 | link | 传递参数 | 获取 | 优点 | 缺点 |
|---|---|---|---|---|---|
| params | <link to="/path/2">xxx</Link> | this.props.history.push({pathname:"/path/" + name}); | this.props.match.params.name | 地址栏刷新不丢失 | 只能传字符串 |
| query | <Link to={{ path : ' /query' , query : { name : 'sunny' }}}> | this.props.history.push({pathname:"/query",query: { name : 'sunny' }}); | this.props.location.query.name | 传参不显示到地址栏 | 刷新参数丢失 |
| state | <Link to={{ path : ' /sort ' , state : { name : 'sunny' }}}> | this.props.history.push({pathname:"/sort ",state : { name : 'sunny' }}); | this.props.location.query.state | 同query | 同query |
| search | <link to="/path/2">xxx</Link> | this.props.history.push({pathname:"/web/departManange?tenantId" + row.tenantId}); | this.props.location.search | 同params | 同params |
v6
子路由形式(对应params)
// 路由文件
<Route path='/list/:id' element={<List />}></Route>
// App.jsx中
<li><Link to="/list/123">列表页</Link></li>
获取
import React from 'react'
import {useParams} from 'react-router-dom'
export default function List() {
const params = useParams()
console.log(params) // {id: '123'}
return <h2>List列表页</h2>
}
问号形式
地址栏携带参数还可以通过问号形式:
<li><Link to="/detail?id=456">详情页</Link></li>
获取该参数的方式,需要使用
useSearchParams这个路由hook,并且调用getAll方法才能获取得到:
import React from 'react'
import {useSearchParams} from 'react-router-dom'
export default function Detail() {
const [params] = useSearchParams()
console.log(params.getAll('id')) // ['456']
return <h2>Detail详情页</h2>
}
state携带
以上两种携带参数的方式,都只能携带简单的参数,如果数据量较大,其实并不方便,因此,在使用
useNavigate()这个hook实现跳转时,其实也可以携带参数:
navigate('/home', {
state: {id: 789}
})
获取该参数的方式便是使用
useLocation这个路由hook:
import React from 'react'
import { useLocation } from 'react-router-dom'
export default function Home() {
const location = useLocation()
console.log(location.state.id) // 789
return <h2>Home首页</h2>
}
404匹配
如果碰上未在路由文件中有做配置的路由,便可认定为404页面:
// 路由组件
import Error from '../pages/Error'
<Route path='/' element={<App />}>
...省略部分路由内容
<Route path='*' element={<Error />}></Route>
</Route>
此时只要是非配置过的页面,便是404页面。