React笔记2——ReactAjax和React路由
三、ReactAjax
1、脚手架配置
-
首先,安装一个轻量级框架axios
yarn add axios
-
在App组件中定义两个简单的按钮分别用来获取学生数据和汽车数据。在测试时,需要打开测试服务器进行监听。
import React, { Component } from 'react' import axios from 'axios' export default class App extends Component { getStudentData = () => { axios.get('http://localhost:3000/api1/students').then( response => {console.log('成功了', response.data);}, error => {console.log('失败了', error);} ) } getCarData = () => { axios.get('http://localhost:3000/api2/cars').then( response => {console.log('成功了', response.data)}, error => {console.log('失败了', error);} ) } render() { return ( <div> <button onClick={this.getStudentData}>点我获取学生数据</button> <button onClick={this.getCarData}>点我获取汽车数据</button> </div> ) } }
-
进行脚手架的配置,在src文件夹下创建setupProxy.js文件。因为配置文件一个项目只需要写一次,所以每次只需要把该文件进行复制粘贴即可。配置文件如下:(该项目因为向两个不同的服务器发送了请求,所以设置了两个代理)
const proxy = require('http-proxy-middleware') module.exports = function(app) { app.use( proxy('/api1', { //遇见'/api1'前缀的请求,就会触发该代理配置 target: 'http://localhost:5000', //请求转发给谁 changeOrigin: true, //控制服务器收到的响应头中Host字段的值(这条写不写影响不大) pathRewrite: {'^/api1':''} //重写请求路径(必须的,用于走代理时将'/api1'换成空字符串) }), proxy('/api2', { target: 'http://localhost:5001', changeOrigin: true, pathRewrite: {'^/api2':''} }) ) }
-
代理配置总结
-
方法1:
在package.json中追加如下配置
"proxy":"http://localhost:5000"
说明:
- 优点:配置简单,前端请求资源时可以不加任何前缀。
- 缺点:不能配置多个代理。
- 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
-
方法2:
-
第一步:创建代理配置文件
在src下创建配置文件:src/setupProxy.js
-
编写setupProxy.js配置具体代理规则:
const proxy = require('http-proxy-middleware') module.exports = function(app) { app.use( proxy('/api1', { //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000) target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址) changeOrigin: true, //控制服务器接收到的请求头中host字段的值 /* changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000 changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000 changeOrigin默认值为false,但我们一般将changeOrigin值设为true */ pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置) }), proxy('/api2', { target: 'http://localhost:5001', changeOrigin: true, pathRewrite: {'^/api2': ''} }) ) }
说明:
- 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
- 缺点:配置繁琐,前端请求资源时必须加前缀。
-
2、github搜索案例
-
将静态页面拆成React的Search、Item、List组件
-
将公共的样式放在App.css中,单独的样式也拆到各自的index.css里(该案例样式较为简单,所以只有List组件中有样式)
-
state都定义在App组件中方便进行修改;App组件中定义了一个修改state的方法updateAppState并将其传给了Search组件
import React, { Component } from 'react' import Search from './components/Search' import List from './components/List' export default class App extends Component { state = { //初始化状态 users: [], //users初始值为数组 isFirst: true, //是否为第一次打开页面 isLoading: false, //标识是否处于加载中 err: '', //存储请求相关的错误信息 } updateAppState = (stateObj) => { this.setState(stateObj) } render() { return ( <div className="container"> <Search updateAppState={this.updateAppState} /> <List {...this.state} /> </div> ) } }
-
Search组件
-
导入axios框架用于向github接口发送get请求;
-
利用ref核心属性给Search实例对象添加keyWordElement属性,该属性对应着input标签。同时keyWordElement是一个对象,它有一个value属性,该属性对应着input标签中输入的文字。
-
我们将该value属性重新赋值为keyword,并通过axios将关键字作为参数发送出去
import React, { Component } from 'react' import axios from 'axios' export default class Search extends Component { search = () => { // 获取用户的输入(连续解构赋值+重命名) const {keyWordElement: {value:keyword}} = this // 发送请求前通知App更新状态 this.props.updateAppState({isFirst:false, isLoading:true}) // 发送网络请求 // 因为我们端口是3001,也从该端口发送请求,所以可以省略http://localhost:3001 axios.get(`/api1/search/users?q=${keyword}`).then( response => { // 请求成功后通知APP更新状态 this.props.updateAppState({isLoading:false, users:response.data.items}) // console.log(response); }, error => { // 请求失败后通知APP更新状态 this.props.updateAppState({isLoading: false, err: error.message, users: []}) // console.log('失败了', error); } ) } render() { return ( <section className="jumbotron"> <h3 className="jumbotron-heading">搜索Github用户</h3> <div> <input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键字点击搜索"/> <button onClick={this.search} >搜索</button> </div> </section> ) } }
-
-
List组件
-
App组件通过展开运算符把state所有属性通过props传递给List组件
-
List通过解构赋值拿到必要的属性
-
jsx只能写js表达式,而不能写for/if/switch这类js语句。故条件判断这里使用三元运算符。重点:三元表达式可以进行连续判断,效果相当于if语句
-
利用map对users数组进行处理,每个元素单独返回,并通过props将所有user属性传给Item组件
import React, { Component } from 'react' import './index.css' import Item from '../Item' export default class List extends Component { render() { const {isFirst, users, isLoading, err} = this.props return ( <div className="row"> { isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> : isLoading ? <h2>加载中...</h2> : err ? <h2 style={{color:'red'}}>{err}</h2> : users.map((user) => { return <Item key={user.id} {...user} /> }) } </div> ) } }
-
-
Item组件
-
对我们需要的user属性进行解构赋值,包括头像链接、用户仓库链接和用户名
-
将属性通过{}放在需要显示数据的位置
import React, { Component } from 'react' export default class Item extends Component { render() { const {avatar_url, html_url, login} = this.props return ( <div className="card"> <a rel="noreferrer" href={html_url} target="_blank"> <img alt='avatar' src={avatar_url} style={{width: '100px'}}/> </a> <p className="card-text">{login}</p> </div> ) } }
-
-
代理文件
const proxy = require('http-proxy-middleware') module.exports = function(app) { app.use( proxy('/api1', { //遇见'/api1'前缀的请求,就会触发该代理配置 target: 'http://localhost:5000', //请求转发给谁 changeOrigin: true, //控制服务器收到的响应头中Host字段的值(这条写不写影响不大) pathRewrite: {'^/api1':''} //重写请求路径(必须的,用于走代理时将'/api1'换成空字符串) }) ) }
-
总结
- 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。
- ES6小知识点:解构赋值+重命名
let obj = {a:{b:1}}
const {a} = obj; //传统解构赋值
const {a:{b}} = obj; //连续解构赋值
const {a:{b:value}} = obj; //连续解构赋值+重命名
3、消息订阅-发布机制
-
使用PubSub
-
安装pubsub-js
yarn install pubsub-js
-
使用方法(https://github.com/mroderick/PubSubJS)
- import PubSub from ‘pubsub-js’ //引入
- PubSub.subscribe(‘delete’, function(data){ }); //订阅
- PubSub.publish(‘delete’, data) //发布消息
-
-
PubSub简化了App组件,因为该方法可以实现兄弟组件间的通信
-
简化的App组件
import React, { Component } from 'react' import Search from './components/Search' import List from './components/List' export default class App extends Component { render() { return ( <div className="container"> <Search /> <List /> </div> ) } }
-
Search组件
-
导入PubSub工具库,导入axios框架
-
使用PubSub.publish()方法发布消息,更新兄弟组件List的state
import React, { Component } from 'react' import PubSub from 'pubsub-js' import axios from 'axios' export default class Search extends Component { search = () => { // 获取用户的输入(连续解构赋值+重命名) const {keyWordElement: {value:keyword}} = this // 发送请求前通知List更新状态 this.props.updateAppState({isFirst:false, isLoading:true}) PubSub.publish('atguigu', {isFirst:false, isLoading:true}) // 发送网络请求 // 因为我们端口是3000,也从该端口发送请求,所以可以省略http://localhost:3000 axios.get(`/api1/search/users?q=${keyword}`).then( response => { // 请求成功后通知List更新状态 PubSub.publish('atguigu', {isLoading:false, users:response.data.items}) }, error => { // 请求失败后通知List更新状态 PubSub.publish('atguigu', {isLoading: false, err: error.message, users: []}) } ) } render() { return ( <section className="jumbotron"> <h3 className="jumbotron-heading">搜索Github用户</h3> <div> <input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键字点击搜索"/> <button onClick={this.search} >搜索</button> </div> </section> ) } }
-
-
List组件
-
state状态放在List组件中
-
在componentDidMount钩子函数中使用PubSub.subscribe()方法接收消息
-
在componentWillUnmount钩子函数中取消订阅
import React, { Component } from 'react' import PubSub from 'pubsub-js' import './index.css' import Item from '../Item' export default class List extends Component { state = { //初始化状态 users: [], //users初始值为数组 isFirst: true, //是否为第一次打开页面 isLoading: false, //标识是否处于加载中 err: '', //存储请求相关的错误信息 } componentDidMount() { this.token = PubSub.subscribe('atguigu', (_, stateObj) => { // console.log('List组件收到数据了', data); this.setState(stateObj) }) } componentWillUnmount() { PubSub.unsubscribe(this.token) } render() { const {isFirst, users, isLoading, err} = this.state return ( <div className="row"> {/* jsx只能写js表达式,而不能写for/if/switch这类js语句。故条件判断这里使用三元运算符。三元表达式可以进行连续判断 */} { isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> : isLoading ? <h2>加载中...</h2> : err ? <h2 style={{color:'red'}}>{err}</h2> : users.map((user) => { return <Item key={user.id} {...user} /> }) } </div> ) } }
-
-
Item组件
Item组件还保持跟之前一样。
四、React路由
1.react-router-dom
-
react的一个插件库。
-
专门用来实现一个SPA应用。
-
基于react的项目基本都会用到此库。
-
安装react-router-dom
yarn add react-router-dom
2.路由的基本使用
-
明确好界面中的导航区、展示区
-
导航区的a标签改为Link标签
<Link to="/xxxxx">Demo</Link>
-
展示区写Route标签进行路径的匹配
<Route path='/xxxx' component={Demo}/>
-
的最外侧包裹了一个或
3.一个简单的路由跳转实例
-
App组件,导入Link和Route
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> <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="/home" component={Home}/> <Route path="/about" component={About}/> </div> </div> </div> </div> </div> ) } }
-
components中的Home和About组件
-
Home组件
import React, { Component } from 'react' export default class Home extends Component { render() { return ( <h3>我是Home的内容</h3> ) } }
-
About组件
import React, { Component } from 'react' export default class About extends Component { render() { return ( <h3>我是About的内容</h3> ) } }
-
4.路由组件与一般组件
1.写法不同:
一般组件:<Demo/>
路由组件:<Route path="/demo" component={Demo}/>
2.存放位置不同:
一般组件:components
路由组件:pages
3.接收到的props不同:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性
history:
go: ƒ go(n)
goBack: ƒ goBack()
goForward: ƒ goForward()
push: ƒ push(path, state)
replace: ƒ replace(path, state)
location:
pathname: "/about"
search: ""
state: undefined
match:
params: {}
path: "/about"
url: "/about"
5.NavLink的使用
NavLink可以实现路由链接的高亮,通过activeClassName指定样式名。
-
将导入的Link改为NavLink
-
若不写activeClassName,则默认对点击的NavLink添加一个active的类。因为这里导入了bootstrap.css样式,里面本身自带有active的样式。
-
若要自定义样式,可在index.html文件中通过style定义样式
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>react脚手架</title> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="stylesheet" href="/css/bootstrap.css"> <style> .leon{ background-color: pink !important; /* color: white !important; */ } </style> </head> <body> <div id="root"></div> </body> </html>
-
App.jsx组件中的NavLink
<NavLink activeClassName="leon" className="list-group-item" to="/home">Home</NavLink> <NavLink activeClassName="leon" className="list-group-item" to="/about">About</NavLink>
-
自定义MyNavLink标签
-
在components创建MyNavLink文件,在这里对NavLink进行一层封装
-
App组件的属性皆可以通过props传送过来
{/* 在React中靠路由链接实现切换组件 */} <MyNavLink to="/home">Home</MyNavLink> <MyNavLink to="/about">About</MyNavLink>
-
MyNavLink标签中的Home、About会作为children属性传送过来,故利用展开运算符,可以将所有属性以简便的方式赋值给NavLink中的属性,如下是MyNavLink组件
import React, { Component } from 'react' import {NavLink} from 'react-router-dom' export default class MyNavLink extends Component { render() { // console.log(this.props); return ( <NavLink activeClassName="leon" className="list-group-item" {...this.props}/> ) } }
-
6.Switch的使用
-
switch的使用
- 通常情况下,path和component是一一对应的关系。
- Switch可以提高路由匹配效率(单一匹配)。
-
举例说明
<Route path="/home" component={Home}/> <Route path="/about" component={About}/> <Route path="/home" component={Test}/>
如上三个路由显示节点,但有两个路径相同的Route。此时若点击MyNavLink所对应的按钮则会将路径相同的两个Route均render到页面上。
此时可以import {Route, Switch} from ‘react-router-dom’,使用Switch标签将Route标签进行包裹,则在匹配到第一个路径时便会停止进行搜索路径了。
-
使用Switch的优点
- 可以提高代码的效率(因为不会重复匹配路径)
7.解决样式丢失问题
-
出现问题的原因
-
想要实现在所有路径前加一个固定的前缀,如公司名atguigu。则会出现css样式丢失的问题。
<MyNavLink to="/atguigu/home">Home</MyNavLink> <MyNavLink to="/atguigu/about">About</MyNavLink> .....其他代码...... <Switch> <Route path="/atguigu/home" component={Home}/> <Route path="/atguigu/about" component={About}/> <Route path="/atguigu/home" component={Test}/> </Switch>
-
-
解决方案
-
打开public文件夹下的index.html
-
默认的bootstap.css样式导入的方法为,该方法的问题是在进行路由跳转时会在路径中加上/atguigu,则会导致css文件找不到
<link rel="stylesheet" href="./css/bootstrap.css">
-
解决方案为:修改默认路径
1.public/index.html 中 引入样式时不写 ./ 写 / (常用) 2.public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用) 3.使用HashRouter
-
8.路由的严格匹配与模糊匹配
-
默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
-
开启严格匹配:在路由中加上exact={true}。也可以就简便写成exact。
//常规写法 <Route exact={true} path="/about" component={About}/> //简便写法 <Route exact path="/about" component={About}/>
-
严格匹配不要随便开启,需要再开**,有些时候开启会导致无法继续匹配二级路由**。
9.Redirect的使用
-
一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由.
-
使用方法:
import {Route, Switch, Redirect} from 'react-router-dom' <Switch> <Route path="/home" component={Home}/> <Route path="/about" component={About}/> <Redirect to="/about" /> </Switch>
10.嵌套路由
-
注册子路由时要写上父路由的path值。
-
路由的匹配是按照注册路由的顺序进行的。
-
使用案例:在路由组件中再导入新的路由组件,这样的话Route的路径便要填写二级路径了。在home路由组件中编写了News和Message两个新路由组件:
import React, { Component } from 'react' import {Route, Switch, Redirect} from 'react-router-dom' import MyNavLink from '../../components/MyNavLink' import Message from './Message' import News from './News' export default class Home extends Component { render() { return ( <div> <h2>Home组件内容</h2> <div> <ul className="nav nav-tabs"> <li> <MyNavLink to="/home/news">News</MyNavLink> </li> <li> <MyNavLink to="/home/message">Message</MyNavLink> </li> </ul> </div> <Switch> <Route path='/home/news' component={News}></Route> <Route path='/home/message' component={Message}></Route> <Redirect to='/home/news'></Redirect> </Switch> </div> ) } }
11.向路由组件传递参数
-
传递params参数:
-
基本使用:
路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link> 注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/> 接收参数:this.props.match.params
-
使用案例:
1.在Home组件中定义Message和News组件
下面是News组件:
import React, { Component } from 'react' export default class News extends Component { render() { return ( <div> <ul> <li>news001</li> <li>news002</li> <li>news003</li> </ul> </div> ) } }
下面是Message组件:定义了state,state中的数据是用来传递给子组件Detail的;利用map方法对数组中的每一个元素单独返回,并定义id属性和to对应的路径;同时将参数传递给子组件;因为要传递的参数是变量,所以使用``而不是’’。
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}> {/* 向路由组件传递params参数 */} <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> </li> ) }) } </ul> <hr/> {/* 声明接收params参数 */} <Route path='/home/message/detail/:id/:title' component={Detail}></Route> </div> ) } }
下面是Detail组件:定义了一个数组变量,用来作为显示信息;通过路径传递过来的参数信息可以通过this.props.match.params获取;使用find方法找出id相对应的数组元素;将找出的结果信息进行render,显示到页面上。
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 }) // console.log(this.props); return ( <ul> <li>ID:{id}</li> <li>TITLE:{title}</li> <li>CONTENT:{findResult.content}</li> </ul> ) } }
-
-
传递search参数
-
基本使用:
路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link> 注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/> 接收参数:this.props.location.search 备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
-
使用案例:
案例还是如上传递params参数的案例。区别点在于:
1.携带参数和注册路由方式不同。
2.传递到Detail组件的参数的获取位置不同。
3.参数在this.props.location中并且是以字符串的形式存在的,如search: “?id=01&title=消息1”
4.所以需要对字符串进行剪切,并借助querystring解析将其转换成对象
携带参数:
{/* 向路由组件传递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>
注册路由:
{/* 声明接收params参数 */} {/* <Route path='/home/message/detail/:id/:title' component={Detail}></Route> */} {/* search参数无需声明接收,正常注册路由即可 */} <Route path='/home/message/detail' component={Detail}></Route>
Detail组件:
import React, { Component } from 'react' import qs from 'querystring' 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 // 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 }) // console.log(this.props); return ( <ul> <li>ID:{id}</li> <li>TITLE:{title}</li> <li>CONTENT:{findResult.content}</li> </ul> ) } }
-
-
传递state参数
-
基本使用:
路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link> 注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/> 接收参数:this.props.location.state 备注:刷新也可以保留住参数
-
使用案例:
案例还是如上传递params参数的案例。区别点在于:
1.携带参数和注册路由方式不同。
2.传递到Detail组件的参数的获取位置不同。
3.参数存在this.props.location.state中
携带参数:
<Link to={{pathname:'/home/message/detail', state: {id: msgObj.id, title: msgObj.title}}}>{msgObj.title}</Link>
注册路由:
{/* state参数无需声明接收,正常注册路由即可 */} <Route path='/home/message/detail' component={Detail}></Route>
Detail组件:
import React, { Component } from 'react' // import qs from 'querystring' 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 // 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 || {} //若state为空,则传递一个空属性。避免报错 const findResult = DetailData.find((detailObj) => { return detailObj.id === id }) || {} // console.log(this.props); return ( <ul> <li>ID:{id}</li> <li>TITLE:{title}</li> <li>CONTENT:{findResult.content}</li> </ul> ) } }
-
12.push与replace模式
-
push模式
push模式也就是一般的使用情况,程序的几次路由跳转会被压进栈中。所以点击后退时会依次返回之前的路由。
-
replace模式
在Link标签中加入replace={true},该路由在点击后则不会留下痕迹。点击回退也不会回到该路由所处位置。(简便模式,直接写一个replace在标签中也行)
<Link replace={true} to={{pathname:'/home/message/detail', state: {id: msgObj.id, title: msgObj.title}}}>{msgObj.title}</Link>
13.编程式路由导航
我们可以通过使用this.props.history对象上的API对操作路由进行跳转、前进和后退操作。
-
实例操作:
在Message组件的消息后面添加两个button按钮;
按钮绑定点击事件,点击事件通过绑定箭头函数传入两个参数;
分别定义使用push和replace两种方式进行跳转的方法;
在Route中定义前进、后退和go方法的按钮;
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'}, ] } pushShow = (id, title) => { //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}) } replaceShow = (id, title) => { //replace跳转+携带params参数 // 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}) } back = () => { // console.log(this.props.history); this.props.history.goBack() } forward = () => { 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}> {/* 向路由组件传递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参数 */} {/* <Route path='/home/message/detail/:id/:title' component={Detail}></Route> */} {/* search参数无需声明接收,正常注册路由即可 */} {/* <Route path='/home/message/detail' component={Detail}></Route> */} {/* state参数无需声明接收,正常注册路由即可 */} <Route path='/home/message/detail' component={Detail}></Route> <button onClick={this.back}>后退</button> <button onClick={this.forward}>前进</button> <button onClick={this.go}>go</button> </div> ) } }
14.withRouter的使用
相比与一般组件,路由组件可以接收到不同的props。
- 一般组件:写组件标签时传递了什么,就能收到什么。
- 路由组件:接收到三个固定的属性history、location、match。
当我们想要一般组件也可以拥有和路由组件一样props时(这样便可以调用props中的方法了。)
实例操作:
-
在Header组件中添加前进、回退两个API按钮
-
导入withouRouter方法,并将调用withRouter方法后的Header组件导出
import React, { Component } from 'react' import {withRouter} from 'react-router-dom' class Header extends Component { back = () => { this.props.history.goBack() } forward = () => { this.props.history.goForward() } render() { console.log(this.props); return ( <div className="page-header"> <h2>React Router Demo</h2> <button onClick={this.back}>回退</button> <button onClick={this.forward}>前进</button> </div> ) } } export default withRouter(Header) // withRouter可以加工一般组件,让一般组件具备路由组件所特有的API // withRouter的返回值是一个新组件
15.BrowserRouter与HashRouter的区别
- 底层原理不一样:
- BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
- HashRouter使用的是URL的哈希值。
- path表现形式不一样
- BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
- HashRouter的路径包含#,例如:localhost:3000/#/demo/test
- 刷新后对路由state参数的影响
- BrowserRouter没有任何影响,因为state保存在history对象中。
- HashRouter刷新后会导致路由state参数的丢失!!!
- 备注:HashRouter可以用于解决一些路径错误相关的问题。
- 比如7中提到的样式丢失问题
五、React——AntDesign组件库的使用
AntDesign官网:https://ant.design/index-cn。可以在官网的“在create-react-app中使用”查看具体的使用步骤。
-
安装依赖:
yarn add antd
-
引入样式:
import 'antd/dist/antd.css'
-
Ant Design样式的使用——按钮
在App组件中添加按钮:
import React, { Component } from 'react' import { Button} from 'antd'; import 'antd/dist/antd.css' export default class App extends Component { render() { return ( <div> <Button type='primary'>主按钮</Button> </div> ) } }
-
Ant Design样式的使用——图标
在App组件中添加图标:
import React, { Component } from 'react' import { Button } from 'antd'; import 'antd/dist/antd.css' import {WechatOutlined, WeiboOutlined, SearchOutlined} from '@ant-design/icons' export default class App extends Component { render() { return ( <div> <WechatOutlined spin={true} style={{fontSize: '50px', color: 'green'}} /> <WeiboOutlined spin={true} style={{fontSize: '50px', color: 'red'}}/> </div> ) } }
-
antd的高级配置:
通过按需引入样式,我们就不需要将样式全部导入了,可以将import 'antd/dist/antd.css’这行代码进行注销。
自定义主题:
按照 配置主题 的要求,自定义主题需要用到类似 less-loader 提供的 less 变量覆盖功能。我们可以引入 craco-less 来帮助加载 less 样式和修改变量。
首先把
src/App.css
文件修改为src/App.less
,然后修改样式引用为 less 文件。/* src/App.js */ - import './App.css'; + import './App.less';
/* src/App.less */ - @import '~antd/dist/antd.css'; + @import '~antd/dist/antd.less';
然后安装
craco-less
并修改craco.config.js
文件如下。$ yarn add craco-less
const CracoLessPlugin = require('craco-less'); module.exports = { plugins: [ { plugin: CracoLessPlugin, options: { lessLoaderOptions: { lessOptions: { modifyVars: { '@primary-color': '#1DA57A' }, javascriptEnabled: true, }, }, }, }, ], };
这里利用了 less-loader 的
modifyVars
来进行主题配置,变量和其他配置方式可以参考 配置主题 文档。修改后重启yarn start
,如果看到一个绿色的按钮就说明配置成功了