路由和SPA
- 传统网页,浏览器根据 url 向服务器请求对应的文件(html/css/js)
- SPA(single-page application) 服务器只专注于提供数据,JS/CSS/HTML打包为一个超集大的文件一次性丢给浏览器, JS劫持浏览器路由,生成虚拟路由来动态渲染页面DOM元素
React路由框架
- 综合性路由框架: react-router
- 浏览器路由框架: react-keeper
- react-native路由框架:react-navigation
react-router
- react-router-dom 用于浏览器,处理Web App的路由
- react-router-native 用于React Native, 处理手机app的路由
- react-router-redux 提供了路由中间件,处理redux的集成
- react-router-config 用来静态配置路由
安装react-router-dom
yarn add react-router-dom
yarn add --dev @types/react-router-dom
BrowserRouter 和 HashRouter的区别:
- BrowserRouter的url不带#, 请求不同的页面都是不同的url, 服务器需要对每个url都做响应配置
- HashRouter带#, #后的内容不会传给服务器,服务器始终响应一个url
react-router已经升级到了v6, 查看详细变化
使用react-router(这里是react-router最新版本6的用法)
// react-router 6 写法
import styles from './App.module.css'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { HomePage, SignInPage, RegisterPage, DetailPage } from './pages'
function App() {
return (
<div className={styles.App}>
<BrowserRouter>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/signIn" element={<SignInPage />} />
<Route path="/register" element={<RegisterPage />} />
<Route path="/detail/:id" element={<DetailPage />} />
<Route path="*" element={<div>404 not found</div>} />
</Routes>
</BrowserRouter>
</div>
)
}
export default App
与 react-router 5 对比
- 5 需要 exact 精确匹配,6已经不需要了
- 6 将 Switch 换成了 Routes
- 6 中render和component都改用element
- 6 中 404 页面
path="*"
```tsx // react-router 5 写法
import React from ‘react’; import styles from “./App.module.css”; import { BrowserRouter, Route, Switch } from “react-router-dom”; import { HomePage, SignInPage, RegisterPage, DetailPage } from “./pages”;
function App() { return (
404 not found 页面去火星了 !
} />export default App;
<a name="s2orp"></a>
#### 如何在URL中添加参数?
- 第一种使用query string: http://localhost/path?name1=value1&name2=value2
- 第二种使用Restful ID: http://localhost/path/:userid
在组件中获取url 参数
```tsx
import { useParams } from 'react-router'
export const DetailPage = () => {
const { touristRouteId } = useParams()
return <h1>产品详情, ID: {touristRouteId}</h1>
}
在 react-router 5 中是可以通过 props 获取参数的,6 已经不可以了
import React from "react";
import { RouteComponentProps } from "react-router-dom";
interface MatchParams {
touristRouteId: string;
}
export const DetailPage: React.FC<RouteComponentProps<MatchParams>> = (
props
) => {
// console.log(props.history);
// console.log(props.location);
// console.log(props.match);
return <h1>路游路线详情页面, 路线ID: {props.match.params.touristRouteId}</h1>;
};
useHistory VS useNavigate
在 react-router 5 中,通过操作 history 实现跳转
// This is a React Router v5 app
import { useHistory } from "react-router-dom";
function App() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
而在 react-router 6 中使用新的API navigate实现跳转
// This is a React Router v6 app
import { useNavigate } from "react-router-dom";
function App() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
用 navigate(to, { replace: true })
替代 history.replace(to)
Redirect VS Navigate
react-router 5 中使用 Redirect 实现路由重定向
<Redirect to="about" /> // 默认是replace
<Redirect to="home" push />
react-router 6 中使用 Navigate 实现路由重定向
<Navigate to="about" replace />
<Navigate to="home" /> // 默认是push
Link
Link也可以实现跳转
<Link to="/invoices">Invoices</Link>
<Link to="/expenses">Expenses</Link>
原理
interface LinkProps {
to: string;
}
const Link: React.FC<LinkProps> = ({children, to}) => {
const history = useHistory()
return (
<a href={to} onClick={()=>{history.push(to)}}>
{children}
</a>
)
}