官方文档:react-router-dom英文建议翻译
v6 与 v5 的区别
安装:yarn add react-router-dom
npm install react-router-dom
react, react-router-dom 以及相关插件版本:
"dependencies": {
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^13.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.2.1",
"react-scripts": "5.0.0",
"web-vitals": "^2.1.4"
}
使用 React Router v6 在根组件下创建路由规则
import { BrowserRouter as Router } from 'react-router-dom';
import Demo from './pages/demo'
import './App.css';
function App() {
return (
<Router>
<Demo />
</Router>
);
}
export default App;
HashRouter 使用的是 URL 的 hash 部分(即 window.location.hash),来保持页面的 UI 与 URL 的同步。
哈希历史记录不支持 location.key 或 location.state
BrowserRouter 使用 HTML5 的 history API(pushState, replaceState 和 popState),让页面的 UI 与 URL 同步。
HashRouter 和 BrowserRouter 的区别
URL 的表现形式不一样 BrowseRouter 使用 HTML5 的 history API,保证 UI 界面和 URL 同步。HashRouter 使用 URL 的哈希部分来保持 UI 和 URL 的同步。
哈希历史记录不支持 location.key 和 location.state。
HashRouter 不支持 location.state 和 location.key。
通过 state 的方式传值给下一个页面的时候,当到达新的页面,刷新或者后退之后再前进,BrowseRouter 传递的值依然可以得到。
上面是使用BrowserRouter模式,特点就是地址没有 # 号,也就说明地址需要经过特殊处理,不然可能刷新页面会404。
demo:
import { Route, Routes, NavLink } from "react-router-dom";
import One from "./one";
import Three from "./three";
import Two from "./two";
import NoFind from "./noFind";
export default function Demo() {
return (
<div>
<h1 style={{ textAlign: "center" }}>我是标题</h1>
<ul>
<li>
<NavLink to="/one">Go One</NavLink>
</li>
<li>
<NavLink to="/two">Go Two</NavLink>
</li>
<li>
<NavLink to="/three">Go Three</NavLink>
</li>
</ul>
<hr />
<Routes>
<Route path="/one" element={One()} />
<Route path="/two/*" element={Two()} />
<Route path="/three" element={Three()} />
<Route path="*" element={NoFind()} />
</Routes>
</div>
);
}
因为之前在App中已经引入过路由规则,所以在demo组件中可以直接使用NavLink等Api,正常使用需要在BrowserRouter标签下。NavLink需要配置to属性,使用引号包裹路径。
在V6中去掉了原来的switch,改为了现在的Routes,而Route中也去掉了components,改为了功能更加强大的element,他可以包裹标签、组件等功能,不要忘了重要的404,必须配置路径为通配符,而且也是必须在最后一位上。
需要嵌套路由的Route上地址必须包括 ‘/*’ 以匹配子路由。
// two.js
import { Routes, Route, NavLink } from 'react-router-dom'
import Five from './five';
import Four from './four';
export default function Two() {
return (
<div>
<h3>我是组件二</h3>
<ul>
<li>
<NavLink to="/two/four">Go Two is four</NavLink>
</li>
<li>
<NavLink to="/two/five">Go Two is five</NavLink>
</li>
</ul>
<hr />
<Routes>
<Route path="/four" element={Four()} />
<Route path="/five" element={Five()} />
</Routes>
</div>
);
}
// four页面
import { Outlet } from 'react-router-dom'
export default function Four() {
return (
<div>
我是组件二的儿子Four
<Outlet />
</div>
)
}
// five 页面
import { Outlet } from "react-router-dom";
export default function Five() {
return (
<div>
我是组件二的儿子Five
<Outlet />
</div>
);
}
值得一提的是对于模块来说,直接导入拼装和之前一样,都是如果是父页面进入子页面时必须要在子页面中添加Outlet这个占位件。
向路由组件传递params参数 :
import * as React from "react";
import {
Route,
Routes,
NavLink,
Link,
BrowserRouter as Router,
useParams,
useLocation,
} from "react-router-dom";
const One = () => {
const {id} = useParams();
const handleClick = () => {
console.log('时间戳', id);
};
return (
<div>
我是组件one
<button onClick={() => handleClick()}>o n e</button>
</div>
);
};
const Two = () => {
const { state } = useLocation();
const handleClick = () => {
console.log("内容", state);
};
return (
<div>
我是组件Two<button onClick={() => handleClick()}>t w o</button>
</div>
);
};
const App = () => {
const id = Date.now()
const content = {
timeId: Date.now(),
txt: '我是标题'
}
return (
<Router>
<h3>我是标题</h3>
<ul>
<li>
<Link to={`/one/${id}`}>11111</Link>
</li>
<li>
<NavLink to={`/two`} state={{content: content, otherNum: {id: '9999', cut: '拿来了!'}}}>
22222
</NavLink>
</li>
</ul>
<Routes path="/one/:id">
<Route path="/one/:id" element={<One />} />
<Route path="/two" element={<Two />} />
</Routes>
</Router>
);
}
export default App;
useParams获取到的值必须是你在Route中指定的:
<Route path="/one/:id" element={<One />} />
const {id} = useParams(); // 可以获取
const {userId} = useParams(); // undefined
假如你要useLocation获取传参,必须将他们放在state中。
注: element中虽然可以使用函数,但是似乎会导致一些hook无法使用, 下图是我同事发给我的,我找了半天也没发现什么问题,最后将函数使用的方式改为标签就可正常获取。
import { Route, Routes, NavLink, useParams } from "react-router-dom";
import One from "./one";
import Three from "./three";
import Two from "./two";
export default function Demo() {
return (
<div>
<h1 style={{ textAlign: "center" }}>我是标题</h1>
<ul>
<li>
<NavLink to="/one/999">Go One</NavLink>
</li>
</ul>
<hr />
<Routes>
<Route path="/one/:id" element={One()} />
</Routes>
</div>
);
}
import { useParams } from "react-router-dom"
export default function One() {
const { id } = useParams()
return <div onClick={() => console.log(id)}>我是组件一</div>;
}
当然,你们应该会发现,V6完全是为函数组件开发而设计的,先不说无法在类组件中使用,现在你想在axios中拦截并让他跳回登录的操作也无法实现了,这肯定会导致一些业务兼容问题,所以我的推荐是,不要用V6,起码等他真正的稳定或开发者想好再用!