React路由
相关理解
SPA的理解
- 单页Web应用(single page web application,SPA)。
- 整个应用只有一个完整的页面。
- 点击页面中的链接不会刷新页面,只会做页面的局部更新。
- 数据都需要通过ajax请求获取, 并在前端异步展现。
路由的理解
- 什么是路由?
- 一个路由就是一个映射关系(key:value)
- key为路径, value可能是function或component
- 路由分类
- 后端路由:
- 理解: value是function, 用来处理客户端提交的请求。
- 注册路由: router.get(path, function(req, res))
- 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
- 前端路由:
- 浏览器端路由,value是component,用于展示页面内容。
- 注册路由: <Route path="/test" component={Test}>
- 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件
- 后端路由:
react-router-dom的理解
- react的一个插件库。
- 专门用来实现一个SPA应用。
- 基于react的项目基本都会用到此库。
react-router-dom相关API
内置组件
- <BrowserRouter>
- <HashRouter>
- <Route>
- <Redirect>
- <Link>
- <NavLink>
- <Switch>
其它
- history对象
- match对象
- withRouter函数
基本路由使用
准备
- 下载react-router-dom: npm install --save react-router-dom
- 引入bootstrap.css: <link rel="stylesheet" href="/css/bootstrap.css">
路由的基本使用
index.js
//引入react核心库
import React from "react";
//引入ReactDOM
import ReactDOM from "react-dom/client";
//
import { BrowserRouter, HashRouter } from "react-router-dom";
//引入App
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<HashRouter>
<App />
</HashRouter>
);
App.jsx
import React, { Component } from "react";
import { Link, Route } from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header">
<h2>React Router Demo</h2>
</div>
</div>
</div>
{/* <BrowserRouter> */}
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠<a>跳转到不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件 */}
<Link className="list-group-item" to="/about">
About
</Link>
<Link className="list-group-item" to="/home">
Home
</Link>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</div>
</div>
</div>
</div>
{/* </BrowserRouter> */}
</div>
);
}
}
About/index.jsx
import React, { Component } from 'react'
export default class index extends Component {
render() {
return (
<h3>我是About的内容</h3>
)
}
}
Home/index.jsx
import React, { Component } from 'react'
export default class index extends Component {
render() {
return (
<h3>我是Home的内容</h3>
)
}
}
NavLink的使用
index.js
//引入react核心库
import React from "react";
//引入ReactDOM
import ReactDOM from "react-dom/client";
//
import { BrowserRouter, HashRouter } from "react-router-dom";
//引入App
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
App.jsx
import React, { Component } from "react";
import { NavLink, Route } from "react-router-dom";
import Home from "./pages/Home"; //Home是路由组件
import About from "./pages/About"; //About是路由组件
import Header from "./components/Header"; //Header是一般组件
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header/>
</div>
</div>
{/* <BrowserRouter> */}
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠<a>跳转到不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件 */}
<NavLink activeClassName="atguigu" className="list-group-item" to="/about">
About
</NavLink>
<NavLink activeClassName="atguigu" className="list-group-item" to="/home">
Home
</NavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</div>
</div>
</div>
</div>
{/* </BrowserRouter> */}
</div>
);
}
}
About/index.jsx
import React, { Component } from 'react'
export default class index extends Component {
render() {
// console.log('About组件收到的props是',this.props);
return (
<h3>我是About的内容</h3>
)
}
}
Home/index.jsx
import React, { Component } from 'react'
export default class index extends Component {
render() {
return (
<h3>我是Home的内容</h3>
)
}
}
封装NavLink
同上部分文件index.js,about,home
App.jsx
import React, { Component } from "react";
import { Route } from "react-router-dom";
import Home from "./pages/Home"; //Home是路由组件
import About from "./pages/About"; //About是路由组件
import Header from "./components/Header"; //Header是一般组件
import MyNavLink from "./components/Header/MyNavLink";
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header/>
</div>
</div>
{/* <BrowserRouter> */}
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠<a>跳转到不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件————编写路由链接 */}
{/* <MyNavLink to="/about" title="About" a={1} b={2} c={3} />
<MyNavLink to="/home" title="Home"/> */}
{/* 把标签名作为标签体放在标签里面 */}
{/* 标签属性,标签体内容 */}
{/* <MyNavLink to="/about" a={1} b={2} c={3}>About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink> */}
{/* 可以把标签体的内容配在children属性中,也就不需要写开始标签与结束标签,直接自闭合就可以了 */}
{/* <NavLink to="/about" children="About"/> */}
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="home">Home</MyNavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</div>
</div>
</div>
</div>
{/* </BrowserRouter> */}
</div>
);
}
}
components/MyNavLin/index.jsx
import React, { Component } from "react";
import { NavLink } from "react-router-dom";
export default class MyNavLink extends Component {
render() {
// props也可以拿到标签体内容
console.log(this.props);
// const {to,title} = this.props
return (
// {...this.props} 这样写将封装的NavLink标签里面的所有属性都带过来了,包括原本可以写在标签体中的children属性
<NavLink activeClassName="atguigu" className="list-group-item" {...this.props} />
);
}
}
components/Header/index.jsx
import React, { Component } from 'react'
export default class Header extends Component {
render() {
// console.log('Header组件收到的props是',this.props);
return (
<div className="page-header">
<h2>React Router Demo</h2>
</div>
)
}
}
Switch的使用
App.jsx
import React, { Component } from "react";
import { Route,Switch } from "react-router-dom";
import Home from "./pages/Home"; //Home是路由组件
import About from "./pages/About"; //About是路由组件
import Header from "./components/Header"; //Header是一般组件
import MyNavLink from "./components/Header/MyNavLink";
import Test from './pages/Test'
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header/>
</div>
</div>
{/* <BrowserRouter> */}
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠<a>跳转到不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件————编写路由链接 */}
{/* <MyNavLink to="/about" title="About" a={1} b={2} c={3} />
<MyNavLink to="/home" title="Home"/> */}
{/* 把标签名作为标签体放在标签里面 */}
{/* 标签属性,标签体内容 */}
{/* <MyNavLink to="/about" a={1} b={2} c={3}>About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink> */}
{/* 可以把标签体的内容配在children属性中,也就不需要写开始标签与结束标签,直接自闭合就可以了 */}
{/* <NavLink to="/about" children="About"/> */}
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="home">Home</MyNavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
{/* 正常情况下 ,应该是path匹配上一个组件之后就不在继续往下匹配了,而目前的情况是path匹配上一个组件之后还会继续往下匹配,将所有匹配上的内容都展示在同一个path中 */}
{/* 最好是让path匹配上一个之后就不继续往下问了,否则的话,继续往下面匹配,当组件特别多的时候,效率就会特别低 */}
{/* 提高效率的方式,使用Switch,匹配上一个就不继续往下匹配了,可以改变顺序测试一下,谁在上面就先显示谁 */}
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Route path="/home" component={Test} />
</Switch>
</div>
</div>
</div>
</div>
{/* </BrowserRouter> */}
</div>
);
}
}
pages/Test/index.jsx
import React, { Component } from 'react'
export default class Test extends Component {
render() {
return (
<div>
<h2>Test...</h2>
</div>
)
}
}
解决样式丢失问题
index.js
//引入react核心库
import React from "react";
//引入ReactDOM
import ReactDOM from "react-dom/client";
//
import { BrowserRouter, HashRouter } from "react-router-dom";
//引入App
import App from "./App";
// 解决样式丢失问题三
//改变路由模式 #后面都是hash值,向3000请求数据的时候就不会带#后面的东西
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
App.jsx
import React, { Component } from "react";
import { Route,Switch } from "react-router-dom";
import Home from "./pages/Home"; //Home是路由组件
import About from "./pages/About"; //About是路由组件
import Header from "./components/Header"; //Header是一般组件
import MyNavLink from "./components/MyNavLink";
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header/>
</div>
</div>
{/* <BrowserRouter> */}
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠<a>跳转到不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件————编写路由链接 */}
{/* <MyNavLink to="/about" title="About" a={1} b={2} c={3} />
<MyNavLink to="/home" title="Home"/> */}
{/* 把标签名作为标签体放在标签里面 */}
{/* 标签属性,标签体内容 */}
{/* <MyNavLink to="/about" a={1} b={2} c={3}>About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink> */}
{/* 可以把标签体的内容配在children属性中,也就不需要写开始标签与结束标签,直接自闭合就可以了 */}
{/* <NavLink to="/about" children="About"/> */}
<MyNavLink to="/atguigu/about">About</MyNavLink>
<MyNavLink to="/atguigu/home">Home</MyNavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Switch>
<Route path="/atguigu/about" component={About} />
<Route path="/atguigu/home" component={Home} />
</Switch>
</div>
</div>
</div>
</div>
{/* </BrowserRouter> */}
</div>
);
}
}
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- ./以当前文件出发,去当前文件去找,这样写的话,在多级路径的情况下,就会把atguigu也带上了,在访问的时候 -->
<!-- <link rel="stylesheet" href="./css/bootstrap.css" /> -->
<!-- /表示的是直接去localhost:3000下面去请求东西,3000/css/bootstrap -->
<!-- 解决样式丢失问题一 -->
<!-- <link rel="stylesheet" href="/css/bootstrap.css" /> -->
<!-- %PUBLIC_URL%代表的是public这个下面的绝对路径 -->
<!-- 解决样式丢失问题二 -->
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css" />
<style>
/* 刚开始点击路由组件高亮的显示并不正常,原因是bootstrap的权限比较高,需要需要加上!important增加权限 */
.atguigu {
background-color: orange !important;
color: white !important;
}
</style>
</head>
<body>
<div id="root"></div>
</body>
</html>
精准匹配与模糊匹配
App.jsx
import React, { Component } from "react";
import { Route,Switch } from "react-router-dom";
import Home from "./pages/Home"; //Home是路由组件
import About from "./pages/About"; //About是路由组件
import Header from "./components/Header"; //Header是一般组件
import MyNavLink from "./components/Header/MyNavLink";
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header/>
</div>
</div>
{/* <BrowserRouter> */}
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠<a>跳转到不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件————编写路由链接 */}
{/* <MyNavLink to="/about" title="About" a={1} b={2} c={3} />
<MyNavLink to="/home" title="Home"/> */}
{/* 把标签名作为标签体放在标签里面 */}
{/* 标签属性,标签体内容 */}
{/* <MyNavLink to="/about" a={1} b={2} c={3}>About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink> */}
{/* 可以把标签体的内容配在children属性中,也就不需要写开始标签与结束标签,直接自闭合就可以了 */}
{/* <NavLink to="/about" children="About"/> */}
<MyNavLink to="/about">About</MyNavLink>
{/* 给多了,但是没要那么多,就是模糊匹配 */}
{/* <MyNavLink to="/home/a/b">Home</MyNavLink> */}
{/* 精准匹配,多给少给都匹配不上 */}
<MyNavLink to="/home">Home</MyNavLink>
{/* 这样匹配不上 */}
{/* <MyNavLink to="/a/home/b">Home</MyNavLink> */}
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Switch>
{/* 精准匹配,多给少给都匹配不上,exact默认true */}
<Route exact={true} path="/about" component={About} />
<Route exact path="/home" component={Home} />
{/* 不匹配 */}
{/* 给少了,匹配的地方更多,就会匹配不上 */}
{/* <Route path="/home/a/b" component={Home} /> */}
</Switch>
</div>
</div>
</div>
</div>
{/* </BrowserRouter> */}
</div>
);
}
}
Redirect的使用
刚进网页页面的时候什么都没有选中
App.jsx
import React, { Component } from "react";
import { Route, Switch,Redirect } from "react-router-dom";
import Home from "./pages/Home"; //Home是路由组件
import About from "./pages/About"; //About是路由组件
import Header from "./components/Header"; //Header是一般组件
import MyNavLink from "./components/Header/MyNavLink";
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header />
</div>
</div>
{/* <BrowserRouter> */}
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 在React中靠路由链接实现切换组件————编写路由链接 */}
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
{/* 一般放在所有路由注册的最下方,当所有路由都无法匹配的时,跳转到Redirect指定的路由 */}
<Redirect to="/about" />
</Switch>
</div>
</div>
</div>
</div>
{/* </BrowserRouter> */}
</div>
);
}
}
嵌套路由使用
嵌套路由的使用
App.jsx
import React, { Component } from "react";
import { Route, Switch,Redirect } from "react-router-dom";
import Home from "./pages/Home"; //Home是路由组件
import About from "./pages/About"; //About是路由组件
import Header from "./components/Header"; //Header是一般组件
import MyNavLink from "./components/MyNavLink";
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header />
</div>
</div>
{/* <BrowserRouter> */}
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 在React中靠路由链接实现切换组件————编写路由链接 */}
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Switch>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
{/* 会导致无法开启二级路由 */}
<Route exact path="/home" component={Home} />
{/* 一般放在所有路由注册的最下方,当所有路由都无法匹配的时,跳转到Redirect指定的路由 */}
<Redirect to="/about" />
</Switch>
</div>
</div>
</div>
</div>
{/* </BrowserRouter> */}
</div>
);
}
}
pages/Home/index.jsx
import React, { Component } from 'react'
//默认暴露,这样引入
import MyNavLink from '../../components/MyNavLink'
import { Route,Switch,Redirect } from 'react-router-dom'
import News from './News'
import Message from './Message'
export default class index extends Component {
render() {
return (
<div>
<h3>我是Home的内容</h3>
<div>
<ul className="nav nav-tabs">
<li>
{/* 由于模糊匹配,可以匹配成功 */}
<MyNavLink to="/home/news">News</MyNavLink>
</li>
<li>
<MyNavLink to="/home/message">Message</MyNavLink>
</li>
</ul>
{/* 注册路由 */}
{/* 如果路径是/home/news下的,就匹配News */}
<Switch>
<Route path="/home/news" component={News} />
<Route path="/home/message" component={Message} />
{/* 会导致无法开启二级路由 */}
{/* <Route exact path="/home/message" component={Message} /> */}
<Redirect to="/home/news" />
</Switch>
</div>
</div>
)
}
}
pages/Home/Message/index.jsx
import React, { Component } from "react";
export default class Message extends Component {
render() {
return (
<div>
<ul>
<li>
<a href="/message1">message001</a>
</li>
<li>
<a href="/message2">message002</a>
</li>
<li>
<a href="/message/3">message003</a>
</li>
</ul>
</div>
);
}
}
pages/Home/News/index.jsx
import React, { Component } from 'react'
export default class News extends Component {
render() {
return (
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
)
}
}
多种路由跳转方式
向路由组件传递params参数
pages/Home/index.jsx
import React, { Component } from 'react'
//默认暴露,这样引入
import MyNavLink from '../../components/MyNavLink'
import { Route,Switch,Redirect } from 'react-router-dom'
import News from './News'
import Message from './Message'
export default class index extends Component {
render() {
return (
<div>
<h3>我是Home的内容</h3>
<div>
<ul className="nav nav-tabs">
<li>
{/* 由于模糊匹配,可以匹配成功 */}
<MyNavLink to="/home/news">News</MyNavLink>
</li>
<li>
<MyNavLink to="/home/message">Message</MyNavLink>
</li>
</ul>
{/* 注册路由 */}
{/* 如果路径是/home/news下的,就匹配News */}
<Switch>
<Route path="/home/news" component={News} />
<Route path="/home/message" component={Message} />
{/* 会导致无法开启二级路由 */}
{/* <Route exact path="/home/message" component={Message} /> */}
<Redirect to="/home/news" />
</Switch>
</div>
</div>
)
}
}
pages/Home/Message/index.jsx
import React, { Component } from "react";
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
messageArr: [
{ id: '01', title: '消息1' },
{ id: '02', title: '消息2' },
{ id: '03', title: '消息3' }
]
}
render() {
const { messageArr } = this.state
return (
<div>
<ul>
{
messageArr.map((msgObj) => {
return (
<li key={msgObj.id}>
{/* 模板字符串是js里面的东西,所以要使用{``},模板字符串里面要混入,就写{} */}
{/* 这样写相当于模糊匹配,上面多,下面少,下面没有接收到 */}
{/* 向路由组件传递params参数 */}
<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
</li>
)
})
}
</ul>
<hr />
{/* 声明接受params参数,前面是path值,后面是传递的id */}
<Route path="/home/message/detail/:id/:title" component={Detail} />
</div>
);
}
}
pages/Home/Message/Detail/index.jsx
import React, { Component } from 'react'
// 在子组件接收需要暂时的参数
const DetailData = [
{id:'01',content: '你好,中国'},
{id:'02',content: '你好,尚硅谷'},
{id:'03',content: '你好,未来的自己'},
]
export default class Detail extends Component {
render() {
const {id,title} = this.props.match.params
const findResult = DetailData.find((detailObj)=>{
return detailObj.id === id
})
return (
<ul>
<li>ID: {id}</li>
<li>TITLE: {title}</li>
<li>CONTENT: {findResult.content}</li>
</ul>
)
}
}
向路由组件传递search参数
pages/Home/Message/index.jsx
import React, { Component } from "react";
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
messageArr: [
{ id: '01', title: '消息1' },
{ id: '02', title: '消息2' },
{ id: '03', title: '消息3' }
]
}
render() {
const { messageArr } = this.state
return (
<div>
<ul>
{
messageArr.map((msgObj) => {
return (
<li key={msgObj.id}>
{/* 模板字符串是js里面的东西,所以要使用{``},模板字符串里面要混入,就写{} */}
{/* 这样写相当于模糊匹配,上面多,下面少,下面没有接收到 */}
{/* 向路由组件传递params参数 */}
{/* <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}
{/* 向路由组件传递search参数 */}
<Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
</li>
)
})
}
</ul>
<hr />
{/* 声明接受params参数,前面是path值,后面是传递的id */}
{/* <Route path="/home/message/detail/:id/:title" component={Detail} /> */}
{/* search参数无需声明接收、正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail} />
</div>
);
}
}
pages/Home/Message/Detail/index.jsx
import React, { Component } from 'react'
import qs from 'querystring'
// let obj ={name:'tom',age:18} //name=tom&age=18 key=value&key=value urlencoded的编码
// console.log(qs.stringify(obj)) //name=tom&age=18
// let str = 'carName=奔驰&price=199'
// console.log(qs.parse(str)); //{carName: '奔驰', price: '199'}
// 在子组件接收需要暂时的参数
const DetailData = [
{id:'01',content: '你好,中国'},
{id:'02',content: '你好,尚硅谷'},
{id:'03',content: '你好,未来的自己'},
]
export default class Detail extends Component {
render() {
console.log(this.props);
//接受params参数
// const {id,title} = this.props.match.params
// 接收search参数
const {search} = this.props.location
const {id,title} = qs.parse(search.slice(1))
const findResult = DetailData.find((detailObj)=>{
return detailObj.id === id
})
return (
<ul>
<li>ID: {id}</li>
<li>TITLE: {title}</li>
<li>CONTENT: {findResult.content}</li>
</ul>
)
}
}
向路由组件传递state参数
pages/Home/Message/index.jsx
import React, { Component } from "react";
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
messageArr: [
{ id: '01', title: '消息1' },
{ id: '02', title: '消息2' },
{ id: '03', title: '消息3' }
]
}
render() {
const { messageArr } = this.state
return (
<div>
<ul>
{
messageArr.map((msgObj) => {
return (
<li key={msgObj.id}>
{/* 传递数据刷新数据不会丢失 */}
{/* 模板字符串是js里面的东西,所以要使用{``},模板字符串里面要混入,就写{} */}
{/* 这样写相当于模糊匹配,上面多,下面少,下面没有接收到 */}
{/* 向路由组件传递params参数 */}
{/* <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}
{/* 向路由组件传递search参数 */}
{/* <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}
{/* 向路由组件传递state参数 */}
{/* 这种方式传递数据刷新数据不会丢失 */}
<Link replace to={{pathname: '/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
</li>
)
})
}
</ul>
<hr />
{/* 声明接受params参数,前面是path值,后面是传递的id */}
{/* <Route path="/home/message/detail/:id/:title" component={Detail} /> */}
{/* search参数无需声明接收、正常注册路由即可 */}
{/* <Route path="/home/message/detail" component={Detail} /> */}
{/* state参数无需声明接收、正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail} />
</div>
);
}
}
pages/Home/Message/Detail/index.jsx
import React, { Component } from 'react'
import qs from 'querystring'
// let obj ={name:'tom',age:18} //name=tom&age=18 key=value&key=value urlencoded的编码
// console.log(qs.stringify(obj)) //name=tom&age=18
// let str = 'carName=奔驰&price=199'
// console.log(qs.parse(str)); //{carName: '奔驰', price: '199'}
// 在子组件接收需要暂时的参数
const DetailData = [
{id:'01',content: '你好,中国'},
{id:'02',content: '你好,尚硅谷'},
{id:'03',content: '你好,未来的自己'},
]
export default class Detail extends Component {
render() {
console.log(this.props);
//接受params参数
// const {id,title} = this.props.match.params
// 接收search参数
// const {search} = this.props.location
// const {id,title} = qs.parse(search.slice(1))
//接收state参数
const {id,title} = this.props.location.state || {}
const findResult = DetailData.find((detailObj)=>{
return detailObj.id === id
}) || {}
return (
<ul>
<li>ID: {id}</li>
<li>TITLE: {title}</li>
<li>CONTENT: {findResult.content}</li>
</ul>
)
}
}
清除历史记录之后(在浏览器中清除)在访问这个网址就会报错了,Uncaught TypeError: Cannot destructure property 'id' of 'this.props.location.state' as it is undefined. 不能读取属性state从undefined中
at Detail.render (index.jsx:22:1)
index.jsx:30Uncaught TypeError: Cannot read properties of undefined (reading 'content') at Detail.render (index.jsx:30:1) 不能读取属性findResult为undefined
使用 || {} 解决,这样就不会报错了
push与replace模式
push与replace的不同
push相当于一种压栈的方式,不替换任何的路径,点击一个记录一个,留下痕迹
给所有路由链接加上replace之后,就不会加上任何记录
开启了replace之后,就是返回之后直接退回到News,而不是退回到message路径
编程式路由导航
pages/Home/News/index.jsx
import React, { Component } from 'react'
export default class News extends Component {
// componentDidMount(){
// console.log('111');
// setTimeout(()=>{
// console.log('111');
// this.props.history.push('/home/message')
// }, 2000)
// }
render() {
return (
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
)
}
}
pages/Home/Message/index.jsx
import React, { Component } from "react";
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
messageArr: [
{ id: '01', title: '消息1' },
{ id: '02', title: '消息2' },
{ id: '03', title: '消息3' }
]
}
replaceShow = (id,title) =>{
// replace跳转+携带params参数
//编写一段代码,让其实现跳转到Detail组件,且为replace跳转、
// this.props.history.replace(`/home/message/detail/${id}/${title}`)
//replace跳转+携带search参数
// this.props.history.replace(`/home/message/detail/?id=${id}&title=${title}`)
//replace跳转+携带state参数
this.props.history.replace('/home/message/detail',{id,title})
}
pushShow = (id,title) =>{
//编写一段代码,让其实现跳转到Detail组件,且为push跳转、
// push跳转+携带params参数
// this.props.history.push(`/home/message/detail/${id}/${title}`)
// push跳转+携带search参数
// this.props.history.push(`/home/message/detail/?id=${id}&title=${title}`)
//replace跳转+携带state参数
this.props.history.push('/home/message/detail',{id,title})
}
back = () =>{
this.props.history.goBack()
}
goForward = () =>{
this.props.history.goForward()
}
go = () =>{
this.props.history.go(-2)
}
render() {
const { messageArr } = this.state
return (
<div>
<ul>
{
messageArr.map((msgObj) => {
return (
<li key={msgObj.id}>
{/* 模板字符串是js里面的东西,所以要使用{``},模板字符串里面要混入,就写{} */}
{/* 这样写相当于模糊匹配,上面多,下面少,下面没有接收到 */}
{/* 向路由组件传递params参数 */}
{/* <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}
{/* 向路由组件传递search参数 */}
{/* <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}
{/* 向路由组件传递state参数 */}
{/* 这种方式传递数据刷新数据不会丢失 */}
<Link to={{pathname: '/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
<button onClick={()=>this.pushShow(msgObj.id,msgObj.title)}>push查看</button>
<button onClick={()=>this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button>
</li>
)
})
}
</ul>
<hr />
{/* 声明接受params参数,前面是path值,后面是传递的id */}
{/* <Route path="/home/message/detail/:id/:title" component={Detail} /> */}
{/* search参数无需声明接收、正常注册路由即可 */}
{/* <Route path="/home/message/detail" component={Detail} /> */}
{/* state参数无需声明接收、正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail} />
<button onClick={this.back}>回退</button>
<button onClick={this.goForward}>前进</button>
<button onClick={this.go}>go</button>
</div>
);
}
}
withRouter的使用
components/Header/index.jsx
import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'
// withRouter解决在一般组件当中使用路由组件API
class Header extends Component {
back = () =>{
this.props.history.goBack() //history是undefined
}
forward = () =>{
this.props.history.goForward()
}
go = () =>{
this.props.history.go(-2)
}
render() {
//如何让this.props拥有history
console.log('Header组件收到的props是',this.props); //{}
return (
<div className="page-header">
<h2>React Router Demo</h2>
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
<button onClick={this.go}>go</button>
</div>
)
}
}
//暴露的是withRouter函数的返回值,作用:withRouter能够接收一个一般组件,然后就把这个一般组件的身上加上了路由组件所特有的那三个属性
export default withRouter(Header)
//withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
// withRouter的返回值是一个新组件
测试代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- 若浏览器不支持js则展示标签中的内容 -->
<noscript>抱歉!您的浏览器不支持js的运行</noscript>
<div id="root"></div>
<script>
//1
// let obj = { a: 1, b: 2 };
// let obj2 = { ...obj, b: 3 };
// console.log(obj2);
//2
// let obj = { a: 1, b: 2 };
// delete obj.a;
// console.log(obj);
//3
let obj = { a: { b: { c: 1 } } };
let obj2 = { a: { b: 1 } };
// console.log(obj.a.b.c);
// const {
// a: {
// b: { c },
// },
// } = obj;
// console.log(c); //1
const {
a: { b: data },
} = obj2;
console.log(data);
</script>
</body>
</html>
额外注意:
按住alt键选中多个方法进行方法定义
安装包的时候最好只使用一种命令,npm i 或者yarn add
前端路由的——history:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>前端路由的基石_history</title>
</head>
<body>
<a href="http://www.atguigu.com" onclick="return push('/test1') ">push test1</a><br><br>
<button onClick="push('/test2')">push test2</button><br><br>
<button onClick="replace('/test3')">replace test3</button><br><br>
<button onClick="back()"><= 回退</button>
<button onClick="forword()">前进 =></button>
<script type="text/javascript" src="https://cdn.bootcss.com/history/4.7.2/history.js"></script>
<script type="text/javascript">
// let history = History.createBrowserHistory() //方法一,直接使用H5推出的history身上的API
let history = History.createHashHistory() //方法二,hash值(锚点)
function push (path) {
history.push(path)
return false
}
function replace (path) {
history.replace(path)
}
function back() {
history.goBack()
}
function forword() {
history.goForward()
}
history.listen((location) => {
console.log('请求路由路径变化了', location)
})
</script>
</body>
</html>