React笔记2——ReactAjax和React路由

React笔记2——ReactAjax和React路由

三、ReactAjax

1、脚手架配置

  1. 首先,安装一个轻量级框架axios

    yarn add axios
    
  2. 在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>
    		)
    	}
    }
    
    
  3. 进行脚手架的配置,在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':''}
        })
      )
    }
    
  4. 代理配置总结

  • 方法1:

    在package.json中追加如下配置

    "proxy":"http://localhost:5000"
    

    说明:

    1. 优点:配置简单,前端请求资源时可以不加任何前缀。
    2. 缺点:不能配置多个代理。
    3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
  • 方法2:

    1. 第一步:创建代理配置文件

      在src下创建配置文件:src/setupProxy.js
      
    2. 编写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': ''}
          })
        )
      }
      

    说明:

    1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
    2. 缺点:配置繁琐,前端请求资源时必须加前缀。

2、github搜索案例

  1. 将静态页面拆成React的Search、Item、List组件

  2. 将公共的样式放在App.css中,单独的样式也拆到各自的index.css里(该案例样式较为简单,所以只有List组件中有样式)

  3. 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>
    		)
    	}
    }
    
    
  4. Search组件

    1. 导入axios框架用于向github接口发送get请求;

    2. 利用ref核心属性给Search实例对象添加keyWordElement属性,该属性对应着input标签。同时keyWordElement是一个对象,它有一个value属性,该属性对应着input标签中输入的文字。

    3. 我们将该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="输入关键字点击搜索"/>&nbsp;
                 <button onClick={this.search} >搜索</button>
              </div>
          </section>
          )
        }
      }
      
      
  5. List组件

    1. App组件通过展开运算符把state所有属性通过props传递给List组件

    2. List通过解构赋值拿到必要的属性

    3. jsx只能写js表达式,而不能写for/if/switch这类js语句。故条件判断这里使用三元运算符。重点:三元表达式可以进行连续判断,效果相当于if语句

    4. 利用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>
          )
        }
      }
      
      
  6. Item组件

    1. 对我们需要的user属性进行解构赋值,包括头像链接、用户仓库链接和用户名

    2. 将属性通过{}放在需要显示数据的位置

      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>
          )
        }
      }
      
  7. 代理文件

    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'换成空字符串)
        })
      )
    }
    
  8. 总结

    1. 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。
    2. ES6小知识点:解构赋值+重命名
      let obj = {a:{b:1}}
      const {a} = obj; //传统解构赋值
      const {a:{b}} = obj; //连续解构赋值
      const {a:{b:value}} = obj; //连续解构赋值+重命名

3、消息订阅-发布机制

  1. 使用PubSub

    1. 安装pubsub-js

      yarn install pubsub-js
      
    2. 使用方法(https://github.com/mroderick/PubSubJS)

      1. import PubSub from ‘pubsub-js’ //引入
      2. PubSub.subscribe(‘delete’, function(data){ }); //订阅
      3. PubSub.publish(‘delete’, data) //发布消息
  2. PubSub简化了App组件,因为该方法可以实现兄弟组件间的通信

  3. 简化的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>
    		)
    	}
    }
    
  4. Search组件

    1. 导入PubSub工具库,导入axios框架

    2. 使用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="输入关键字点击搜索"/>&nbsp;
                 <button onClick={this.search} >搜索</button>
              </div>
          </section>
          )
        }
      }
      
      
  5. List组件

    1. state状态放在List组件中

    2. 在componentDidMount钩子函数中使用PubSub.subscribe()方法接收消息

    3. 在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>
          )
        }
      }
      
      
  6. Item组件

    Item组件还保持跟之前一样。

四、React路由

1.react-router-dom

  • react的一个插件库。

  • 专门用来实现一个SPA应用。

  • 基于react的项目基本都会用到此库。

  • 安装react-router-dom

    yarn add react-router-dom
    

2.路由的基本使用

  1. 明确好界面中的导航区、展示区

  2. 导航区的a标签改为Link标签

    <Link to="/xxxxx">Demo</Link>
    
  3. 展示区写Route标签进行路径的匹配

    <Route path='/xxxx' component={Demo}/>
    
  4. 的最外侧包裹了一个或

3.一个简单的路由跳转实例

  1. 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>
    		)
    	}
    }
    
    
    
  2. components中的Home和About组件

    1. Home组件

      import React, { Component } from 'react'
      
      export default class Home extends Component {
        render() {
          return (
            <h3>我是Home的内容</h3>
          )
        }
      }
      
      
    2. 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指定样式名。

  1. 将导入的Link改为NavLink

  2. 若不写activeClassName,则默认对点击的NavLink添加一个active的类。因为这里导入了bootstrap.css样式,里面本身自带有active的样式。

  3. 若要自定义样式,可在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>
    
  4. 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>
    
  5. 自定义MyNavLink标签

    1. 在components创建MyNavLink文件,在这里对NavLink进行一层封装

    2. App组件的属性皆可以通过props传送过来

      					{/* 在React中靠路由链接实现切换组件 */}
      							<MyNavLink to="/home">Home</MyNavLink>
      							<MyNavLink to="/about">About</MyNavLink>
      
    3. 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的使用

  1. switch的使用

    • 通常情况下,path和component是一一对应的关系。
    • Switch可以提高路由匹配效率(单一匹配)。
  2. 举例说明

    <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标签进行包裹,则在匹配到第一个路径时便会停止进行搜索路径了。

  3. 使用Switch的优点

    • 可以提高代码的效率(因为不会重复匹配路径)

7.解决样式丢失问题

  1. 出现问题的原因

    • 想要实现在所有路径前加一个固定的前缀,如公司名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>
      
  2. 解决方案

    • 打开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.路由的严格匹配与模糊匹配

  1. 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)

  2. 开启严格匹配:在路由中加上exact={true}。也可以就简便写成exact。

    //常规写法
    <Route exact={true} path="/about" component={About}/>
    //简便写法
    <Route exact path="/about" component={About}/>
    
  3. 严格匹配不要随便开启,需要再开**,有些时候开启会导致无法继续匹配二级路由**。

9.Redirect的使用

  1. 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由.

  2. 使用方法:

    import {Route, Switch, Redirect} from 'react-router-dom'
    
    <Switch>
    	<Route path="/home" component={Home}/>
    	<Route path="/about" component={About}/>
    	<Redirect to="/about" />
    </Switch>
    
    

10.嵌套路由

  1. 注册子路由时要写上父路由的path值。

  2. 路由的匹配是按照注册路由的顺序进行的。

  3. 使用案例:在路由组件中再导入新的路由组件,这样的话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.向路由组件传递参数

  1. 传递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>
          )
        }
      }
      
      
  2. 传递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>
          )
        }
      }
      
      
  3. 传递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>
    
                      &nbsp;<button onClick={() => {this.pushShow(msgObj.id, msgObj.title)}}>push查看</button>
                      &nbsp;<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的区别

  1. 底层原理不一样:
    • BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
    • HashRouter使用的是URL的哈希值。
  2. path表现形式不一样
    • BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
    • HashRouter的路径包含#,例如:localhost:3000/#/demo/test
  3. 刷新后对路由state参数的影响
    • BrowserRouter没有任何影响,因为state保存在history对象中。
    • HashRouter刷新后会导致路由state参数的丢失!!!
  4. 备注:HashRouter可以用于解决一些路径错误相关的问题。
    • 比如7中提到的样式丢失问题

五、React——AntDesign组件库的使用

​ AntDesign官网:https://ant.design/index-cn。可以在官网的“在create-react-app中使用”查看具体的使用步骤。

  1. 安装依赖:

    yarn add antd
    
  2. 引入样式:

    import 'antd/dist/antd.css'
    
  3. 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>
    		)
    	}
    }
    
    
  4. 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>
    		)
    	}
    }
    
    
  5. 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-loadermodifyVars 来进行主题配置,变量和其他配置方式可以参考 配置主题 文档。修改后重启 yarn start,如果看到一个绿色的按钮就说明配置成功了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值