一、路由的使用
1.声明式导航
- 在
src/index.js
文件中定义一个路由模式(可选,也可以在具体的某个组件中使用Router)
javascript
import React from "react";
import ReactDOM from "react-dom";
// 设置路由模式
import {HashRouter as Router} from 'react-router-dom'
// 定义 provider
import { Provider } from "react-redux";
import store from "./Store/index";
import App from "./App";
ReactDOM.render(
<Provider store={store}>
{/* 使用Router包裹根组件 */}
<Router>
<App></App>
</Router>
</Provider>,
document.getElementById("root")
);
-
在根组件
src/App.js
中引入路由相关组件(根据自身需要选择路由模式),并使用- 后续除了特殊的路由规则(嵌套路由)以外,其它普通的路由规则都需要在根组件中运用
javascript
import React, { Component } from "react";
import { HashRouter as Router, Route, Link } from "react-router-dom";
import Cmp10 from "./Components/Cmp10";
import Cmp11 from "./Components/Cmp11";
class App extends Component {
render() {
return (
<Router>
<div>
<h1>导航区域</h1>
<div>
<ul>
<li>
<Link to="/home">首页</Link>
</li>
<li>
<Link to="/news">新闻</Link>
</li>
</ul>
</div>
</div>
<Route path="/home" component={Cmp10}></Route>
<Route path="/news" component={Cmp11}></Route>
</Router>
);
}
}
export default App;
在写上述代码时注意,路由自带组件的顺序嵌套关系,组件<Link></Link>
和组件<Route></Route>
必须被组件<Router></Router>
给包裹着。
需要注意:
刨除样式的影响,
Route
组件在HTML代码中的位置决定了渲染后其在页面中显示的位置。如果Route
放在最后,则其显示的时候也在最后;若其放在渲染内容的最前面,相应的显示也会在最开始。
2.编程式导航
react-router-dom中通过history对象中的push/go等方法实现编程式导航功能,这一点与之前的vue路由还是很相似的。
javascript
this.props.history.push({
pathname: "/home",
search: "from=404", // 表示传递查询字符串
state: { // 隐式传参,地址栏不体现
username: "admin",
},
});
// 给定给定的数字(正数或负数)决定去往历史栈中的哪个地址,正数往未来,负数往过去
this.props.history.go(-1)
this.props.history.goBack(-1)
请勿在根组件中写编程式导航,因为根组件默认是没有props对象,解决办法见后续。
案例:
javascript
import React, { Component } from "react";
import { parseSearch } from "../utils/function";
export default class index extends Component {
// 编程式导航
render() {
console.log(this.props);
const { push } = this.props.history;
// 接收query参数
const { search } = this.props.location;
// 获取路径中的参数转为对象格式 1. 使用new URL 拼接路径 + 需要转换的参数
const urlObj = new URL("http://localhost" + search);
// 2.使用Object.fromEntries 将urlObj.searchParams 转换 为一个新的对象
console.log(Object.fromEntries(urlObj.searchParams));
// 精简写法
console.log(
Object.fromEntries(new URL("http://localhost" + search).searchParams)
);
// 封装写法
console.log(parseSearch(search));
return (
<div>
{/* <button onClick={() => push("/news")}>去新闻页</button> */}
<button
onClick={() =>
push({
pathname: "/news",
search: "id=100",
state: { login: true },
})
}
>
去新闻页
</button>
</div>
);
}
}
二、路由参数
路由参数:在Route定义渲染组件时给定动态绑定的参数。
React路由传参方式有三种:
-
==动态路由参数(param)==
-
以"/film/detail/:id"形式传递的数据
-
在目标页面路由中传递
-
在落地组件中通过
this.props.match.params
得到 -
一般用于restful规范下的开发
-
-
查询字符串(query)
-
通过地址栏中的
?key=value&key=value
传递 -
在落地组件中通过
this.props.location.search
得到 -
由于得到的数据是带"?"的,还需要进一步加工处理之后才能使用,因此建议少用或者不用
-
-
隐式传参(state),通过地址栏是观察不到的
-
不合适写在声明式导航中,写在编程式导航中更加合适
-
一般数用于埋点数据
- 简单的讲,埋点是将部分标记隐藏起来,等待用户去触发,因为这个事情不想让用户看到(需要做一些数据的收集,后续做分析),因此会使用隐式传参的方式(大数据分析)
-
在落地组件中通过
this.props.location.state
得到
-
接收示例:
javascript
constructor(props){
super(props)
this.state = {
// 接收动态路由参数
news_id: this.props.match.params.id,
// 接收查询字符串并处理
query: querystring.parse(this.props.location.search.slice(1)),
// 接收state
state: this.props.location.state
};
}
案例:
首页:
javascript
import React, { Component } from "react";
import { NavLink } from "react-router-dom";
import { Route } from "react-router-dom/cjs/react-router-dom.min";
import index from "./pages/index";
import news from "./pages/news";
// 样式
import './assets/css/App.css'
export default class App extends Component {
render() {
return (
<div>
<ul>
{/* 声明式导航 标签式导航 */}
<li>
{/* 字符串传参 */}
<NavLink to="/index?name=index">首页</NavLink>
</li>
<li>
{/* 路径传参 */}
<NavLink to="/news/100">新闻页</NavLink>
</li>
</ul>
<hr />
<Route path="/index" component={index}></Route>
{/* 在匹配params 动态参数时 需要设置传递名称 ?表示可传可不传*/}
<Route path="/news/:id?" component={news}></Route>
</div>
);
}
}
index页面
javascript
import React, { Component } from "react";
import { parseSearch } from "../utils/function";
export default class index extends Component {
// 编程式导航
render() {
console.log(this.props);
const { push } = this.props.history;
// 接收query参数
const { search } = this.props.location;
// 获取路径中的参数转为对象格式 1. 使用new URL 拼接路径 + 需要转换的参数
const urlObj = new URL("http://localhost" + search);
// 2.使用Object.fromEntries 将urlObj.searchParams 转换 为一个新的对象
console.log(Object.fromEntries(urlObj.searchParams));
// 精简写法
console.log(
Object.fromEntries(new URL("http://localhost" + search).searchParams)
);
// 封装写法
console.log(parseSearch(search));
return (
<div>
{/* <button onClick={() => push("/news")}>去新闻页</button> */}
<button
onClick={() =>
push({
pathname: "/news",
search: "id=100",
state: { login: true },
})
}
>
去新闻页
</button>
</div>
);
}
}
news页面
javascript
import React, { Component } from "react";
class news extends Component {
render() {
// 获取params 参数
console.log(this.props.match.params);
// 获取state 参数
console.log(this.props.location.state);
return <div>news</div>;
}
}
export default news
三、重定向与404路由
1.重定向路由
React的重定向路由有以下写法:
在重定向的时候需要知道,从哪里来,到哪里去,因此该组件需要使用2个属性:
from:匹配需要重定向的路由
to:需要去往的路由
javascript
import { Redirect } from "react-router-dom"
<Redirect from="/from" to="/to"></Redirect>
注意:
-
建议使用Route书写路由规则时,使用Switch组件包裹Route组件,以便最多只能匹配一个规则
-
建议给Redirect组件加上exact属性,来设置严格匹配模式
案例:
javascript
import React, { Component } from "react";
import { NavLink, Redirect,Switch } from "react-router-dom";
import { Route } from "react-router-dom/cjs/react-router-dom.min";
import index from "./pages/index";
import news from "./pages/news";
// 样式
import "./assets/css/App.css";
export default class App extends Component {
render() {
return (
<div>
<ul>
{/* 声明式导航 标签式导航 */}
<li>
{/* 字符串传参 */}
<NavLink to="/index?name=index">首页</NavLink>
</li>
<li>
{/* 路径传参 */}
<NavLink to="/news/100">新闻页</NavLink>
</li>
</ul>
<hr />
<Switch>
{/* 重定向 exact 严格匹配 配合switch 匹配一箱 */}
<Redirect from="/" to="/index" exact></Redirect>
<Route path="/index" component={index}></Route>
{/* 在匹配params 动态参数时 需要设置传递名称 ?表示可传可不传*/}
<Route path="/news/:id?" component={news}></Route>
</Switch>
</div>
);
}
}
2.404路由
项目中少不了404页面的配置,在React里面配置404页面需要注意:
- 需要用到Switch组件,让其去包裹路由的
Route
组件(Switch组件保证只渲染其中一个子路由)
javascript
import NotFound from "./Components/404";
<Route>
<NotFound></NotFound>
</Route>
// 或
<Route component={NotFound}></Route>
注意:在404路由的位置,不需要给定具体的路由匹配规则,不给
path
表示匹配*
,即所有的路由都会匹配,因此用404路由一定要加Switch
匹配一个路由。注意:并不会因为当前是404路由/重定向路由而改变状态码,因为当前写的是前端的内容,状态码是后端提供的,只有等后期上线以后才能有状态码。
案例:
javascript
import React, { Component } from "react";
import { NavLink,Switch } from "react-router-dom";
import { Route } from "react-router-dom/cjs/react-router-dom.min";
import index from "./pages/index";
import news from "./pages/news";
import NotFound from "./components/NotFound";
// 样式
import "./assets/css/App.css";
export default class App extends Component {
render() {
return (
<div>
<ul>
{/* 声明式导航 标签式导航 */}
<li>
{/* 字符串传参 */}
<NavLink to="/index?name=index">首页</NavLink>
</li>
<li>
{/* 路径传参 */}
<NavLink to="/news/100">新闻页</NavLink>
</li>
</ul>
<hr />
<Switch>
<Route path="/index" component={index}></Route>
{/* 在匹配params 动态参数时 需要设置传递名称 ?表示可传可不传*/}
<Route path="/news/:id?" component={news}></Route>
{/* 404路由必须加switch 在版本留替换为 Routes*/}
<Route path="*" component={NotFound}></Route>
</Switch>
</div>
);
}
}
404页面
javascript
import React, { Component } from "react";
export default class NotFound extends Component {
componentDidMount() {
const {goBack} = this.props.history
// 执行副作用
setTimeout(() => {
goBack()
},3000)
}
render() {
return <div>404 NotFound</div>;
}
}
四、路由渲染方式和withRouter
v6中采用了新的属性对组件进行渲染,属性名:element
Route路由渲染组件是用于路由规则匹配成功后组件渲染容器,此组件提供了3种方式组件渲染方式:
-
component属性(组件对象/函数)
javascript<Route path="/home" component={Home} />
javascript<Route path="/home" component={(props) => <Home />} />
-
render属性(函数)
javascript<Route path="/home" render={(props) => <Home />} />
-
children属性(组件/函数)
javascript<Route path="/about" children={(props) => { if(props.match){ return <div>children渲染</div> } }} />
javascript<Route path="/about" children={<About />} />
注意点
当children的值是一个函数时,无论当前地址和path路径匹不匹配,都将会执行children对应的函数,当children的值为一个组件时,当前地址和path不匹配时,路由组件不渲染
children函数方式渲染,会在形参中接受到一个对象,对象中match属性如果当前地址匹配成功返回对象,否则null
withRouter
作用:把不是通过路由切换过来的组件中,将react-router 的 history、location、match 三个对象传入props对象上
默认情况下,必须是经过路由匹配渲染的组件才存在this.props,才拥有路由参数,才能使用编程式导航的写法,才能执行this.props.history.push('/uri')跳转到对应路由的页面。然而不是所有组件都直接与路由相连的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,此时就可以使用this.props。
javascript
// 引入withRouter
import { withRouter} from 'react-router-dom'
// 执行一下withRouter
export default withRouter(Cmp)
该高阶组件是路由包自带的东西,因此只需要引入+使用就可以了,不需要自己定义。
V5与V6的变化
-
Switch被废弃,由Routes替代,并且Routes是必须的
-
渲染组件的方式被element属性替代,例如:
javascript<Route path="/dashboard" element={<Dashboard />} />
-
重定向组件Redirect被废弃,使用以下语法替代:
javascript<Route path="/" element={<Navigate to="/dashboard/welcome" />} />
-
嵌套路由被简化
-
父路由:
javascript<Route path="/dashboard/*" element={<Dashboard />} />
-
子路由(子路由规则中不再需要写父前缀):
javascript<Route path="welcome" element={<Welcome/>}/>
-
- 废弃了withRouter
案例:
主页面
javascript
import React, { Component } from "react";
import { NavLink,Switch } from "react-router-dom";
import { Route } from "react-router-dom/cjs/react-router-dom.min";
import index from "./pages/index";
import News from "./pages/news";
import NotFound from "./components/NotFound";
// 样式
import "./assets/css/App.css";
export default class App extends Component {
render() {
return (
<div>
<ul>
{/* 声明式导航 标签式导航 */}
<li>
{/* 字符串传参 */}
<NavLink to="/index?name=index">首页</NavLink>
</li>
<li>
{/* 路径传参 */}
<NavLink to="/news/100">新闻页</NavLink>
</li>
</ul>
<hr />
<Switch>
<Route path="/index" component={index}></Route>
{/* 在匹配params 动态参数时 需要设置传递名称 ?表示可传可不传*/}
{/* <Route path="/news/:id?" component={News}></Route> */}
{/* 函数渲染方式 (不常用) 如果使用该方法传参 搭配 withRouter 包装使用*/}
{/* <Route path="/news/:id?" component={(props)=><News/>}></Route> */}
{/* render 函数渲染方式 (不常用) 如果使用该方法传参 搭配 withRouter 包装使用*/}
{/* <Route path="/news/:id?" render={()=><News></News>}></Route> */}
{/* children 函数渲染方式 (不常用) 如果使用该方法传参 搭配 withRouter 包装使用*/}
<Route path="/news/:id?" children={<News/>}></Route>
{/* children 组件渲染方式 (不常用) */}
<Route path="/news/:id?" children={News}></Route>
{/* 404路由必须加switch 在版本留替换为 Routes*/}
<Route path="*" component={NotFound}></Route>
</Switch>
</div>
);
}
}
子页面
javascript
import React, { Component } from "react";
// import { withRouter } from "react-router-dom";
// class news extends Component {
// render() {
// // 获取params 参数
// console.log(this.props.match.params);
// // 获取state 参数
// console.log(this.props.location.state);
// return <div>news</div>;
// }
// }
// export default withRouter(news)
class news extends Component {
render() {
// 获取params 参数
console.log(this.props.match.params);
// 获取state 参数
console.log(this.props.location.state);
return <div>news</div>;
}
}
export default news