React全家桶学习

article/2025/11/6 20:02:40

来源:尚硅谷视频教程 - 张天禹

一、React简介

1 react特点

React:用于构建用户界面的javascript库。是一个将数据渲染为HTML视图的开源JavaScript库。

react的特点:

1.采用组件化模式、声明式编码,提高开发效率及组件复用率。

2.在React Native中可以使用React语法进行移动端开发。

3.使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互。

2 react基本使用

2.1 相关js库

  1. react.js:React核心库。
  2. react-dom.js:提供操作DOM的react扩展库。
  3. babel.min.js:解析JSX语法代码转为JS代码的库。
  4. prop-types.js:用于对组件标签属性进行限制

2.2 创建虚拟DOM的两种方式

  1. 纯JS方式(一般不用)
  2. JSX方式

2.3 虚拟DOM与真实DOM

  1. 本质是Object类型的对象(一般对象)
  2. 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在使用,无需真实DOM上那么多属性。
  3. 虚拟DOM最终会被React转化成真实DOM,呈现在页面上

2.4 jsx语法规则

1.介绍

        (1)全称:  JavaScript XML

        (2)react定义的一种类似于XML的JS扩展语法: JS + XML本质是                          React.createElement(componentprops, ...children)方法的语法糖

        (3)作用: 用来简化创建虚拟DOM

2.规则

        (1)定义虚拟dom时,不要写引号

        (2)标签中混入JS表达式时要用{}

        (3)样式的类名要用className

        (4)内联样式要用style={{}}的形式去写

        (5)只有一个根标签

        (6)标签必须闭合

        (7)标签首字母

                A.若小写字母开头,则将该标签转为html中同名标签,若html中无该标签对应的

                 同名的元素,则报错。

                B.若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。

const myID = 'aTguigu'
const myData = 'hello,React'
const VDOM = (<div><h2 className="title" id={myID.toLowerCase()}><span style={{color:'white',fontSize:'30px'}}>{myData.toUpperCase()}</span></h2><h2 className="title" id={myID.toLowerCase() + '2'}><span style={{color:'white',fontSize:'30px'}}>{myData.toUpperCase()}</span></h2></div>
)
ReactDOM.render(VDOM,document.getElementById('test'))

额外知识:

1. 区分js表达式和js语句

        (1)js表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方

                例如:a 、a+b、demo()、arr.map()、function test()

        (2)js语法(代码):

                例如:if语句、for循环、switch语句

2.react的jsx语法里的{},可以自动遍历数组,不能遍历obj

3.react定义一个数组,如需遍历生成<li></li>,使用data.map()

const data = ['Angular','React','Vue']
const VDOM = (<h1>前端js框架列表</h1><ul>{data.map((item,index) =>{return <li key={index}>{item}</li>})}</ul> 
)

2.5 模块与组件、模块化与组件化的理解

1.模块

        (1)理解:向外提供特定功能的js程序,一般就是一个js文件

        (2)为什么要拆成模块:随着业务逻辑的增加,代码越来越多且复杂

        (3)作用:复用js,简化js的编写,提高js的效率

2.组件

        (1)理解:用来实现局部功能效果的代码和资源的集合

        (2)为什么:一个界面的功能更复杂

        (3)作用:复用编码,简化项目编码,提高运行效率

3.模块化

当应用的js都已模块来编写,这个应用就是一个模块化的应用

4.组件化

当应用是以多组件的方式实现,这个应用就是一个组件化的应用

二、react面向组件编程

1  基本理解和使用

(1)使用react开发者工具调试

(2)组件的定义

        A.函数式组件:

                ReactDOM.render(<Demo/>......)之后发生了什么:

                        ① React解析组件标签,找到了Demo组件

                        ② 发现组件是使用函数定义的,调用该函数,将返回的虚拟DOM转为真实DOM

function Demo(){console.log(this) //此处的this是underfined,因为babel编译后开启了严格模式return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
ReactDOM.render(<Demo/>,document.getElementById('test'))

        B.类式组件

                ReactDOM.render(<Demo/>......)之后发生了什么:

                        ① React解析组件标签,找到了Demo组件

                        ② 发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型                              上的render方法。将render返回的虚拟DOM转为真实DOM,呈现在页面上。

class Demo extends React.Commonent {render(){// render 是放在哪里的?    -- Demo的原型对象上,供实例使用// render中的this是谁?     -- Demo的实例对象 return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>}
}
ReactDOM.render(<Demo/>,document.getElementById('test'))

2 组件实例的三大核心属性:state

2.1 理解

(1)state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)

(2)组件被成为“状态机”,通过更改state来更新对应的页面的显示(重新渲染组件)

2.2 注意

(1)组件中的render方法中的this为组件实例对象

(2)组件自定义的方法中this为underfined,如何解决?

        a.强制绑定this,通过函数对象的bind()

        b.箭头函数

(3)状态数据,不能直接修改或更新

class Weather extends React.Component {constructor(props){super(props)// 初始化状态this.state = {isHot:true}// 解决changeWeather中this指向问题  this.changeWeather = this.changeWeather.bind(this)}render(){console.log(this)return <h1 onClick={this.changeWeather} className='title'>今天天气很{this.state.isHot ? '炎热' : '寒冷'}</h1>}changeWeather(){// changeWeather放在哪里? -- Weather的原型对象上,供实例使用// 由于changeWeather是作为onClick的回调,所以changeWeather不是通过实例调用,是直接调用// 类中的方法默认开启了局部的严格模式,所以changeWeather中的this为underfined// 获取原来的isHot值const isHot = this.state.isHot// 严重注意:状态(state)不可直接更改,要借助一个setState去更改// setState更改是合并state的值,不是替换this.setState({isHot:!isHot})}
}
ReactDOM.render(<Weather/>,document.getElementById('test'))
class Weather extends React.Component {state = {isHot:true}render(){return <h1 onClick={this.changeWeather} className='title'>今天天气很{this.state.isHot ? '炎热' : '寒冷'}</h1>}// 自定义方法 -- 要用赋值语句的形式 + 箭头函数changeWeather = ()=>{const isHot = this.state.isHotthis.setState({isHot:!isHot})}
}
ReactDOM.render(<Weather/>,document.getElementById('test'))

3 组件实例的三大核心属性:props

3.1 实例中使用props(类式组件)

class Person extends React.Component{render(){const {name,age,sex} = this.propsreturn(<ul><li>姓名:{name}</li><li>性别:{sex}</li><li>年龄:{age}</li></ul>)}
}
//对标签属性类型进行类型、必要性的限制 
Person.propTypes = {name:PropTypes.string.isRequired, //限制name属性为字符串,并且必传sex:PropTypes.string, //限制name属性为字符串age:PropTypes.number, //限制name属性为数值speak:PropTypes.func //限制name属性为函数
}
//指定默认的标签属性值
Person.defaultProps = {sex:'男',age:18
}
ReactDOM.render(<Person name='jerry' speak={speak} />,document.getElementById('test1'))
ReactDOM.render(<Person name='tom' age={18} sex='女' />,document.getElementById('test2'))const p = {name:'老刘',age:18,sex:'女'}
ReactDOM.render(<Person {...p} />,document.getElementById('test3'))function speak(){console.log('我说话了')
}
class Person extends React.Component{static propTypes = {name:PropTypes.string.isRequired, sex:PropTypes.string, age:PropTypes.number,speak:PropTypes.func}static defaultProps = {sex:'男',age:18}state = {}render(){const {name,age,sex} = this.propsreturn(<ul><li>姓名:{name}</li><li>性别:{sex}</li><li>年龄:{age}</li></ul>)}
}ReactDOM.render(<Person name='jerry' speak={speak} />,document.getElementById('test1'))
ReactDOM.render(<Person name='tom' age={18} sex='女' />,document.getElementById('test2'))const p = {name:'老刘',age:18,sex:'女'}
ReactDOM.render(<Person {...p} />,document.getElementById('test3'))function speak(){console.log('我说话了')
}

注意:类中写构造器的情况,只有在构造器需要用到this.props访问props才写。

         类中构造器可以不写的情况下尽量不写。

         构造器是否接收props,是否传递给super,取决于:是佛希望在构造器中通过this访问props

class Person extends React.Component{constructor(props){super(props)console.log('constructor',this.props)}
}

3.2 函数式组件中使用props

function Person(props){
const {name,age,sex} = propsreturn(<ul><li>姓名:{name}</li><li>性别:{sex}</li><li>年龄:{age}</li></ul>)
}
Person.propTypes = {name:PropTypes.string.isRequired, sex:PropTypes.string, age:PropTypes.number,speak:PropTypes.func
}
Person.defaultProps = {sex:'男',age:18
}
ReactDOM.render(<Person name='jerry' age={19} sex='男' />,document.getElementById('test'))

3.3 理解和作用

(1)每个组件对象都会有props(properties的简写)属性

(2)组件标签的所有属性都保存在props中

(3)通过标签属性从组件外向组件内传递变化的数据

(4)注意:组件内部不要修改props数据

4 组件实例的三大核心属性:refs

4.1 字符串形式的refs(被弃用)

<input ref="input1" type="text" placeholder="点击按钮提示数据" />

4.2 函数回调形式的refs

(1)内联形式的函数回调(大多数情况下使用这种方式)

 <input ref={c => this.input1 = c;console.log('@',c)} type="text" placeholder="点击按钮提示数据" />

        注意:在更新过程中函数会被执行两次,第一次传入参数null,第二次传入DOM元素,即

                   第一次输出@,=null;第二次输出@,DOM元素

        原因:在每一次渲染时会创建一个新的函数实例,所以React清空旧的ref并设置新的。

(2)class的绑定函数(解决更新过程中被执行两次)

<input ref={this.saveRef} type="text" placeholder="点击按钮提示数据" />saveRef = (c)=>{this.input1 = c;console.log('@',c)
}

3.3 React.createRef(官网推荐使用)

class Demo extends React.Component{myRef = React.createRef()myRef2 = React.createRef()render(){return(<div><input ref={this.myRef} type="text" placeholder="点击按钮提示数据" /><button onClick={this.showData}>点我提示左侧的数据</button><input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据" /></div>)}showData = ()=>{alert(this.myRef.current.value)}showData2 = ()=>{alert(this.myRef2.current.value)}  
}
ReactDOM.render(<Demo/>,document.getElementById('test'))

注意:React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,

           该容器是“专人专用”,一个ref标识只能创建有一个容器。

5 事件处理

(1)通过onXxx属性指定事件处理函数(注意大小写)

      1.React使用的是自定义(合成事件),而不是使用的原生DOM事件  -- 为了更好的兼容性

        2.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) -- 为了高效

(2)通过event.target得到发生事件的Dom元素对象  --  不要过度使用ref

class Demo extends React.Component{
myRef = React.createRef()
render(){return(<div><input ref={this.myRef} type="text" placeholder="点击按钮提示数据" /><button onClick={this.showData}>点我提示左侧的数据</button><input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" /></div>)
}
showData = ()=>{alert(this.myRef.current.value)
}
showData2 = (event)=>{alert(event.target.value)
}  
}
ReactDOM.render(<Demo/>,document.getElementById('test'))

 6 收集表单数据 - 阶函数与函数柯里化

(1)高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数

        1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数

        2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数

(2)函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数           编码形式

(3)函数柯里化写法:

 class Demo extends React.Component{state = {username:'',password:''}handleSubmit = (event)=>{event.preventDefault()alert(`用户名是:${this.state.username},密码是:${this.state.password}`)}saveFormData = (dataType)=>{return (event) => {this.setState({[dataType]:event.target.value})}}render(){return(<form onSubmit={this.handleSubmit}>用户名:<input onChange={this.saveFormData('username')} type="text" name="username" />密码<input onChange={this.saveFormData('password')} type="password" name="password" /><button>提交</button></form>)}
}
ReactDOM.render(<Demo/>,document.getElementById('test'))

(4)不用函数柯里化写法:

class Demo extends React.Component{state = {username:'',password:''}handleSubmit = (event)=>{event.preventDefault()alert(`用户名是:${this.state.username},密码是:${this.state.password}`)}saveFormData = (dataType,event)=>{this.setState({[dataType]:event.target.value})}render(){return(<form onSubmit={this.handleSubmit}>用户名:<input onChange={ event => this.saveFormData('username',event) } type="text" name="username" />密码<input onChange={ event => this.saveFormData('password',event) } type="password" name="password" /><button>提交</button></form>)}
}
ReactDOM.render(<Demo/>,document.getElementById('test'))

7 生命周期

1.组件从创建到死亡会经历一些特定的阶段

2.React组件中包含一系列钩子函数,会在特定的时期调用

3.我们在定义组件时,会在特定的声明周期回调函数中做特定的事情

三、react脚手架

1 初始化脚手架

npm i create-react-app -g
create-react-app 项目名

2 todoList案例

(1)app.js

import React, { Component } from 'react'
import Header from './components/Header'
import List from './components/List'
import Footer from './components/Footer'
import './App.css'export default class App extends Component {//状态在哪里,操作状态的方法就在哪里//初始化状态state = {todos:[{id:'001',name:'吃饭',done:true},{id:'002',name:'睡觉',done:true},{id:'003',name:'打代码',done:false},{id:'004',name:'逛街',done:false}]}//addTodo用于添加一个todo,接收的参数是todo对象addTodo = (todoObj)=>{//获取原todosconst {todos} = this.state//追加一个todoconst newTodos = [todoObj,...todos]//更新状态this.setState({todos:newTodos})}//updateTodo用于更新一个todo对象updateTodo = (id,done)=>{//获取状态中的todosconst {todos} = this.state//匹配处理数据const newTodos = todos.map((todoObj)=>{if(todoObj.id === id) return {...todoObj,done}else return todoObj})this.setState({todos:newTodos})}//deleteTodo用于删除一个todo对象deleteTodo = (id)=>{//获取原来的todosconst {todos} = this.state//删除指定id的todo对象const newTodos = todos.filter((todoObj)=>{return todoObj.id !== id})//更新状态this.setState({todos:newTodos})}//checkAllTodo用于全选checkAllTodo = (done)=>{//获取原来的todosconst {todos} = this.state//加工数据const newTodos = todos.map((todoObj)=>{return {...todoObj,done}})//更新状态this.setState({todos:newTodos})}//clearAllDone用于清除所有已完成的clearAllDone = ()=>{//获取原来的todosconst {todos} = this.state//过滤数据const newTodos = todos.filter((todoObj)=>{return !todoObj.done})//更新状态this.setState({todos:newTodos})}render() {const {todos} = this.statereturn (<div className="todo-container"><div className="todo-wrap"><Header addTodo={this.addTodo}/><List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo}/><Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone}/></div></div>)}
}

(2)Header组件

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {nanoid} from 'nanoid'
import './index.css'export default class Header extends Component {//对接收的props进行:类型、必要性的限制static propTypes = {addTodo:PropTypes.func.isRequired}//键盘事件的回调handleKeyUp = (event)=>{//解构赋值获取keyCode,targetconst {keyCode,target} = event//判断是否是回车按键if(keyCode !== 13) return//添加的todo名字不能为空if(target.value.trim() === ''){alert('输入不能为空')return}//准备好一个todo对象const todoObj = {id:nanoid(),name:target.value,done:false}//将todoObj传递给Appthis.props.addTodo(todoObj)//清空输入target.value = ''}render() {return (<div className="todo-header"><input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/></div>)}
}

(3)List组件

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'export default class List extends Component {//对接收的props进行:类型、必要性的限制static propTypes = {todos:PropTypes.array.isRequired,updateTodo:PropTypes.func.isRequired,deleteTodo:PropTypes.func.isRequired,}render() {const {todos,updateTodo,deleteTodo} = this.propsreturn (<ul className="todo-main">{todos.map( todo =>{return <Item key={todo.id} {...todo} updateTodo={updateTodo} deleteTodo={deleteTodo}/>})}</ul>)}
}

(4)Item组件

import React, { Component } from 'react'
import './index.css'export default class Item extends Component {state = {mouse:false} //标识鼠标移入、移出//鼠标移入、移出的回调handleMouse = (flag)=>{return ()=>{this.setState({mouse:flag})}}//勾选、取消勾选某一个todo的回调handleCheck = (id)=>{return (event)=>{this.props.updateTodo(id,event.target.checked)}}//删除一个todo的回调handleDelete = (id)=>{if(window.confirm('确定删除吗?')){this.props.deleteTodo(id)}}render() {const {id,name,done} = this.propsconst {mouse} = this.statereturn (<li style={{backgroundColor:mouse ? '#ddd' : 'white'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}><label><input type="checkbox" checked={done} onChange={this.handleCheck(id)}/><span>{name}</span></label><button onClick={()=> this.handleDelete(id) } className="btn btn-danger" style={{display:mouse?'block':'none'}}>删除</button></li>)}
}

(5)Footer组件

import React, { Component } from 'react'
import './index.css'export default class Footer extends Component {//全选checkbox的回调handleCheckAll = (event)=>{this.props.checkAllTodo(event.target.checked)}//清除已完成任务的回调handleClearAllDone = ()=>{this.props.clearAllDone()}render() {const {todos} = this.props//已完成的个数const doneCount = todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0),0)//总数const total = todos.lengthreturn (<div className="todo-footer"><label><input type="checkbox" onChange={this.handleCheckAll} checked={doneCount === total && total !== 0 ? true : false}/></label><span><span>已完成{doneCount}</span> / 全部{total}</span><button onClick={this.handleClearAllDone} className="btn btn-danger">清除已完成任务</button></div>)}
}

四、React AJAX

1 理解

(1)前置说明

1. React本身只关注页面,并不包含发送ajax请求的代码

2. 前端应用需要通过ajax请求与后台进行交互

3. react应用中需要集成第三方ajax库(或自己封装)

(2)常用的ajax请求库

1. Jqyery:比较重,如果需要另外引入,不建议使用

2. axios:轻量级,建议使用

        a. 封装XmlHttpRequest对象的ajax

        b.  promise风格

        c. 可以在浏览器端和node服务器端

2 react配置proxy代理

(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:5000changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000changeOrigin默认值为false,但我们一般将changeOrigin值设为true*/pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)}),proxy('/api2', { target: 'http://localhost:5001',changeOrigin: true,pathRewrite: {'^/api2': ''}}))}

        说明:

        1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。

        2. 缺点:配置繁琐,前端请求资源时必须加前缀。

3 axios发送请求

	search = ()=>{//获取用户的输入(连续解构赋值+重命名)const {keyWordElement:{value:keyWord}} = this//发送请求前通知App更新状态this.props.updateAppState({isFirst:false,isLoading:true})//发送网络请求axios.get(`/api1/search/users?q=${keyWord}`).then(response => {//请求成功后通知App更新状态this.props.updateAppState({isLoading:false,users:response.data.items})},error => {//请求失败后通知App更新状态this.props.updateAppState({isLoading:false,err:error.message})})}

4 fetch发送请求

	search = async ()=>{try {const response= await fetch(`/api1/search/users2?q=${keyWord}`)const data = await response.json()console.log(data);PubSub.publish('atguigu',{isLoading:false,users:data.items})} catch (error) {console.log('请求出错',error);PubSub.publish('atguigu',{isLoading:false,err:error.message})}}

五 react路由

1 路由基本使用

注意:由于react-router-dom在2021年11月升级到6版本,而教程使用的是5版本

npm i react-router-dom@5

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

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

<Link to="/xxxxx">Demo</Link>

3.展示区写Route标签进行路径的匹配

<Route path='/xxxx' component={Demo}/>

4.<App>的最外侧包裹了一个<BrowserRouter>或<HashRouter>

ReactDOM.render(<BrowserRouter><App/></BrowserRouter>,document.getElementById('root')
)
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="/about" component={About}/><Route path="/home" component={Home}/></div></div></div></div></div>)}
}

2 路由组件与一般组件

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"

3 NavLink与封装NavLink

1. NavLink可以实现路由链接的高亮,通过activeClassName指定样式名  

.atguigu{color:red
}<NavLink activeClassName="atguigu" className="list-group-item" to="/about">About</NavLink>

2. 封装NavLink

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="atguigu" className="list-group-item" {...this.props}/>)}
}
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>

3. 标签体内容是一个特殊的标签属性

4. 通过this.props.children可以获取标签体内容

4 Switch的使用 -- 改用Routers

1.通常情况下,path和component是一一对应的关系。

2.Switch可以提高路由匹配效率(单一匹配)。

3.两个以上的路由就使用Switch包裹路由

<Switch><Route path="/about" component={About}/><Route path="/home" component={Home}/><Route path="/home" component={Test}/>
</Switch>

5 解决多级路径刷新页面样式丢失的问题

<MyNavLink to="/api/about">About</MyNavLink>

1.public/index.html 中 引入样式时不写 ./ 写 / (常用)

2.public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)-- 只适用脚手架

3.使用HashRouter

6 路由的严格匹配与模糊匹配

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

2.开启严格匹配:

<Route exact={true} path="/about" component={About}/>

3.严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

7 Redirect的使用    

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

2.具体编码:

<Switch><Route path="/about" component={About}/><Route path="/home" component={Home}/><Redirect to="/about"/></Switch>

8 嵌套路由

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

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

export default class Home 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>{/* 注册路由 */}<Switch><Route path="/home/news" component={News}/><Route path="/home/message" component={Message}/><Redirect to="/home/news"/></Switch></div></div>)}
}

 9 向路由组件传递参数

(1)params参数

a. 路由链接(携带参数):

<Link to='/demo/test/tom/18'}>详情</Link>

b. 注册路由(声明接收):

<Route path="/demo/test/:name/:age" component={Test}/>

c. 接收参数:

this.props.match.params

export default class Message extends Component {state = {messageArr:[{id:'01',title:'消息1'},{id:'02',title:'消息2'},{id:'03',title:'消息3'},]}render() {const {messageArr} = this.statereturn (<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}/></div>)}
}

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.paramsconst findResult = DetailData.find((detailObj)=>{return detailObj.id === id})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解析

export default class Message extends Component {state = {messageArr:[{id:'01',title:'消息1'},{id:'02',title:'消息2'},{id:'03',title:'消息3'},]}render() {const {messageArr} = this.statereturn (<div><ul>{messageArr.map((msgObj)=>{return (<li key={msgObj.id}>{/* 向路由组件传递search参数 */}<Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link></li>)})}</ul><hr/>{/* search参数无需声明接收,正常注册路由即可 */}<Route path="/home/message/detail" component={Detail}/></div>)}
}
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() {console.log(this.props);// 接收search参数const {search} = this.props.locationconst {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>)}
}

(3)state参数

路由链接(携带参数):

<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>

 注册路由(无需声明,正常注册即可):

<Route path="/demo/test" component={Test}/>

接收参数:

this.props.location.state

 备注:刷新也可以保留住参数

export default class Message extends Component {state = {messageArr:[{id:'01',title:'消息1'},{id:'02',title:'消息2'},{id:'03',title:'消息3'},]}render() {const {messageArr} = this.statereturn (<div><ul>{messageArr.map((msgObj)=>{return (<li key={msgObj.id}>{/* 向路由组件传递state参数 */}<Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link></li>)})}</ul><hr/>{/* state参数无需声明接收,正常注册路由即可 */}<Route path="/home/message/detail" component={Detail}/></div>)}
}
const DetailData = [{id:'01',content:'你好,中国'},{id:'02',content:'你好,尚硅谷'},{id:'03',content:'你好,未来的自己'}
]
export default class Detail extends Component {render() {console.log(this.props);// 接收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>)}
}

10 编程式路由导航

借助this.props.history对象上的API对操作路由跳转、前进、后退

                            -this.props.history.push()

                            -this.props.history.replace()

                            -this.props.history.goBack()

                            -this.props.history.goForward()

                            -this.props.history.go()

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参数//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)=>{//push跳转+携带params参数// this.props.history.push(`/home/message/detail/${id}/${title}`)//push跳转+携带search参数// this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)//push跳转+携带state参数this.props.history.push(`/home/message/detail`,{id,title})}back = ()=>{this.props.history.goBack()}forward = ()=>{this.props.history.goForward()}go = ()=>{this.props.history.go(-2)}render() {const {messageArr} = this.statereturn (<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}/> */}{/* search参数无需声明接收,正常注册路由即可 */}{/* <Route path="/home/message/detail" component={Detail}/> */}{/* state参数无需声明接收,正常注册路由即可 */}<Route path="/home/message/detail" component={Detail}/><button onClick={this.back}>回退</button>&nbsp;<button onClick={this.forward}>前进</button>&nbsp;<button onClick={this.go}>go</button></div>)}
}
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>)}
}

11 withRouter的使用

withRouter可以加工一般组件,让一般组件具备路由组件所特有的API

withRouter返回的是一个新组件

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()}go = ()=>{this.props.history.go(-2)}render() {console.log('Header组件收到的props是',this.props);return (<div className="page-header"><h2>React Router Demo</h2><button onClick={this.back}>回退</button>&nbsp;<button onClick={this.forward}>前进</button>&nbsp;<button onClick={this.go}>go</button></div>)}
}export default withRouter(Header)

12 BrowserRouter与HashRouter的区别

1.底层原理不一样:

        BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。

         HashRouter使用的是URL的哈希值。

2.path表现形式不一样

        BrowserRouter的路径中没有#,例如:localhost:3000/demo/test

        HashRouter的路径包含#,例如:localhost:3000/#/demo/test

3.刷新后对路由state参数的影响

         (1).BrowserRouter没有任何影响,因为state保存在history对象中。

         (2).HashRouter刷新后会导致路由state参数的丢失!!!

 4.备注:HashRouter可以用于解决一些路径错误相关的问题。

六 redux

1 redux简介

(1)redux是什么

        1. redux是一个专门用做状态管理的js库

        2. 它可以用在react,angular,vue等项目中,但基本和react配合使用

        3. 作用:集中式管理react应用中多个组件共享的状态

(2)什么情况下用redux

        1. 某个状态的状态,需要让其他组件可以随时拿到(共享)

        2. 一个组件需要改变另一个组件的状态(通信)

        3. 总体原则:能不用就不用,如果不用比较吃力才使用

2 redux原理

3 redux的使用

3.1 同步action

1. store.js 

1).引入redux中的createStore函数,创建一个store

2).createStore调用时要传入一个为其服务的reducer

 3).记得暴露store对象

新版本使用这种方式引入store

import { legacy_createStore as createStore} from 'redux'
/* 该文件专门用于暴露一个store对象,整个应用只有一个store对象
*///引入createStore,专门用于创建redux中最为核心的store对象
import {createStore} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//暴露store
export default createStore(countReducer)

 2.count_reducer.js

1).reducer的本质是一个函数,接收:preState,action,返回加工后的状态

2).reducer有两个作用:初始化状态,加工状态

3).reducer被第一次调用时,是store自动触发的,

        传递的preState是undefined,

         传递的action是:{type:'@@REDUX/INIT_a.2.b.4}

/* 1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT,DECREMENT} from './constant'const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){// console.log(preState);//从action对象中获取:type、dataconst {type,data} = action//根据type决定如何加工数据switch (type) {case INCREMENT: //如果是加return preState + datacase DECREMENT: //若果是减return preState - datadefault:return preState}
}

3.count_action.js

专门用于创建action对象

/* 该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from './constant'export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})

4.constant.js

放置容易写错的type值

/* 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

5.index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'ReactDOM.render(<App/>,document.getElementById('root'))store.subscribe(()=>{ReactDOM.render(<App/>,document.getElementById('root'))
})

3.2 异步action

 1. store.js 

/* 该文件专门用于暴露一个store对象,整个应用只有一个store对象
*///引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//暴露store
export default createStore(countReducer,applyMiddleware(thunk))

 2.count_reducer.js        

/* 1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT,DECREMENT} from './constant'const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){// console.log(preState);//从action对象中获取:type、dataconst {type,data} = action//根据type决定如何加工数据switch (type) {case INCREMENT: //如果是加return preState + datacase DECREMENT: //若果是减return preState - datadefault:return preState}
}

3.count_action.js

/* 该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from './constant'//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const createIncrementAsyncAction = (data,time) => {return (dispatch)=>{setTimeout(()=>{dispatch(createIncrementAction(data))},time)}
}

4.constant.js

/* 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

5.count组件

import React, { Component } from 'react'
//引入store,用于获取redux中保存状态
import store from '../../redux/store'
//引入actionCreator,专门用于创建action对象
import {createIncrementAction,createDecrementAction,createIncrementAsyncAction
} from '../../redux/count_action'export default class Count extends Component {state = {carName:'奔驰c63'}/* componentDidMount(){//检测redux中状态的变化,只要变化,就调用renderstore.subscribe(()=>{this.setState({})})} *///加法increment = ()=>{const {value} = this.selectNumberstore.dispatch(createIncrementAction(value*1))}//减法decrement = ()=>{const {value} = this.selectNumberstore.dispatch(createDecrementAction(value*1))}//奇数再加incrementIfOdd = ()=>{const {value} = this.selectNumberconst count = store.getState()if(count % 2 !== 0){store.dispatch(createIncrementAction(value*1))}}//异步加incrementAsync = ()=>{const {value} = this.selectNumber// setTimeout(()=>{store.dispatch(createIncrementAsyncAction(value*1,500))// },500)}render() {return (<div><h1>当前求和为:{store.getState()}</h1><select ref={c => this.selectNumber = c}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select>&nbsp;<button onClick={this.increment}>+</button>&nbsp;<button onClick={this.decrement}>-</button>&nbsp;<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;<button onClick={this.incrementAsync}>异步加</button>&nbsp;</div>)

七 react-redux

 

7.1 完整写法

1. index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'ReactDOM.render(<App/>,document.getElementById('root'))//监测redux中状态的改变,如redux的状态发生了改变,那么重新渲染App组件
store.subscribe(()=>{ReactDOM.render(<App/>,document.getElementById('root'))
})

 2.app.jsx

import React, { Component } from 'react'
import Count from './containers/Count'
import store from './redux/store'export default class App extends Component {render() {return (<div>{/* 给容器组件传递store */}<Count store={store} /></div>)}

3.store.js

/* 该文件专门用于暴露一个store对象,整个应用只有一个store对象
*///引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//暴露store
export default createStore(countReducer,applyMiddleware(thunk))

4. count_reducer.js

/* 1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT,DECREMENT} from './constant'const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){// console.log(preState);//从action对象中获取:type、dataconst {type,data} = action//根据type决定如何加工数据switch (type) {case INCREMENT: //如果是加return preState + datacase DECREMENT: //若果是减return preState - datadefault:return preState}
}

5. constant.js

/* 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

6. count/-action.js

/* 该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from './constant'//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const createIncrementAsyncAction = (data,time) => {return (dispatch)=>{setTimeout(()=>{dispatch(createIncrementAction(data))},time)}
}

7. containers\Count

//引入Count的UI组件
import CountUI from '../../components/Count'
//引入action
import {createIncrementAction,createDecrementAction,createIncrementAsyncAction
} from '../../redux/count_action'//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'/* 1.mapStateToProps函数返回的是一个对象;2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value3.mapStateToProps用于传递状态
*/
function mapStateToProps(state){return {count:state}
}/* 1.mapDispatchToProps函数返回的是一个对象;2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value3.mapDispatchToProps用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch){return {jia:number => dispatch(createIncrementAction(number)),jian:number => dispatch(createDecrementAction(number)),jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),}
}//使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)

8. components\Count

import React, { Component } from 'react'export default class Count extends Component {state = {carName:'奔驰c63'}//加法increment = ()=>{const {value} = this.selectNumberthis.props.jia(value*1)}//减法decrement = ()=>{const {value} = this.selectNumberthis.props.jian(value*1)}//奇数再加incrementIfOdd = ()=>{const {value} = this.selectNumberif(this.props.count % 2 !== 0){this.props.jia(value*1)}}//异步加incrementAsync = ()=>{const {value} = this.selectNumberthis.props.jiaAsync(value*1,500)}render() {//console.log('UI组件接收到的props是',this.props);return (<div><h1>当前求和为:{this.props.count}</h1><select ref={c => this.selectNumber = c}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select>&nbsp;<button onClick={this.increment}>+</button>&nbsp;<button onClick={this.decrement}>-</button>&nbsp;<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;<button onClick={this.incrementAsync}>异步加</button>&nbsp;</div>)}
}

7,2 containers\Count的简化

import React, { Component } from 'react'
//引入action
import {createIncrementAction,createDecrementAction,createIncrementAsyncAction
} from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'//定义UI组件
class Count extends Component {state = {carName:'奔驰c63'}//加法increment = ()=>{const {value} = this.selectNumberthis.props.jia(value*1)}//减法decrement = ()=>{const {value} = this.selectNumberthis.props.jian(value*1)}//奇数再加incrementIfOdd = ()=>{const {value} = this.selectNumberif(this.props.count % 2 !== 0){this.props.jia(value*1)}}//异步加incrementAsync = ()=>{const {value} = this.selectNumberthis.props.jiaAsync(value*1,500)}render() {//console.log('UI组件接收到的props是',this.props);return (<div><h1>当前求和为:{this.props.count}</h1><select ref={c => this.selectNumber = c}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select>&nbsp;<button onClick={this.increment}>+</button>&nbsp;<button onClick={this.decrement}>-</button>&nbsp;<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;<button onClick={this.incrementAsync}>异步加</button>&nbsp;</div>)}
}//使用connect()()创建并暴露一个Count的容器组件
export default connect(state => ({count:state}),//mapDispatchToProps的一般写法/* dispatch => ({jia:number => dispatch(createIncrementAction(number)),jian:number => dispatch(createDecrementAction(number)),jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),}) *///mapDispatchToProps的简写{jia:createIncrementAction,jian:createDecrementAction,jiaAsync:createIncrementAsyncAction,}
)(Count)

 7.3 react-redux优化

(1).容器组件和UI组件整合一个文件

(2).无需自己给容器组件传递store,给<App/>包裹一个<Provider store={store}>即可。

(3).使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。

(4).mapDispatchToProps也可以简单的写成一个对象

(5).一个组件要和redux“打交道”要经过哪几步?

         (1).定义好UI组件---不暴露

           (2).引入connect生成一个容器组件,并暴露,写法如下:

                 connect(

                 state => ({key:value}), //映射状态

                {key:xxxxxAction} //映射操作状态的方法

                 )(UI组件)

(4).在UI组件中通过this.props.xxxxxxx读取和操作状态

1. index.js -- 使用Provider传递 store、不用使用store.subscribe

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import {Provider} from 'react-redux'ReactDOM.render(<Provider store={store}><App/></Provider>,document.getElementById('root')
)

2. app.js -- 不用传递store

import React, { Component } from 'react'
import Count from './containers/Count'export default class App extends Component {render() {return (<div><Count/></div>)}
}

3. containers\ Conut

import React, { Component } from 'react'
//引入action
import {createIncrementAction,createDecrementAction,createIncrementAsyncAction
} from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'//定义UI组件
class Count extends Component {state = {carName:'奔驰c63'}//加法increment = ()=>{const {value} = this.selectNumberthis.props.jia(value*1)}//减法decrement = ()=>{const {value} = this.selectNumberthis.props.jian(value*1)}//奇数再加incrementIfOdd = ()=>{const {value} = this.selectNumberif(this.props.count % 2 !== 0){this.props.jia(value*1)}}//异步加incrementAsync = ()=>{const {value} = this.selectNumberthis.props.jiaAsync(value*1,500)}render() {//console.log('UI组件接收到的props是',this.props);return (<div><h1>当前求和为:{this.props.count}</h1><select ref={c => this.selectNumber = c}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select>&nbsp;<button onClick={this.increment}>+</button>&nbsp;<button onClick={this.decrement}>-</button>&nbsp;<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;<button onClick={this.incrementAsync}>异步加</button>&nbsp;</div>)}
}//使用connect()()创建并暴露一个Count的容器组件
export default connect(state => ({count:state}),//mapDispatchToProps的一般写法/* dispatch => ({jia:number => dispatch(createIncrementAction(number)),jian:number => dispatch(createDecrementAction(number)),jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),}) *///mapDispatchToProps的简写{jia:createIncrementAction,jian:createDecrementAction,jiaAsync:createIncrementAsyncAction,}
)(Count)

7.4 数据共享版

1.index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import {Provider} from 'react-redux'ReactDOM.render(<Provider store={store}><App/></Provider>,document.getElementById('root')
)

2. app.js

import React, { Component } from 'react'
import Count from './containers/Count'
import Person from './containers/Person'export default class App extends Component {render() {return (<div><Count/><hr/><Person/></div>)}
}

2.redux/actions

        (1)count.js

/* 该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from '../constant'//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const createIncrementAsyncAction = (data,time) => {return (dispatch)=>{setTimeout(()=>{dispatch(createIncrementAction(data))},time)}
}

        (2)person.js

import {ADD_PERSON} from '../constant'//创建增加一个人的action动作对象
export const createAddPersonAction = personObj => ({type:ADD_PERSON,data:personObj})

3. redux/reducers

        (1)count.js

/* 1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT,DECREMENT} from '../constant'const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){// console.log('countReducer@#@#@#');//从action对象中获取:type、dataconst {type,data} = action//根据type决定如何加工数据switch (type) {case INCREMENT: //如果是加return preState + datacase DECREMENT: //若果是减return preState - datadefault:return preState}
}

        (2)person.js

import {ADD_PERSON} from '../constant'//初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]export default function personReducer(preState=initState,action){// console.log('personReducer@#@#@#');const {type,data} = actionswitch (type) {case ADD_PERSON: //若是添加一个人return [data,...preState]default:return preState}
}

4. redux/constant.js

/* 该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON = 'add_person'

5. redux/store.js

/* 该文件专门用于暴露一个store对象,整个应用只有一个store对象
*///引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware,combineReducers} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './reducers/count'
//引入为Count组件服务的reducer
import personReducer from './reducers/person'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'//汇总所有的reducer变为一个总的reducer
const allReducer = combineReducers({he:countReducer,rens:personReducer
})//暴露store
export default createStore(allReducer,applyMiddleware(thunk))

6.containers/Count/index.jsx

import React, { Component } from 'react'
//引入action
import {createIncrementAction,createDecrementAction,createIncrementAsyncAction
} from '../../redux/actions/count'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'//定义UI组件
class Count extends Component {state = {carName:'奔驰c63'}//加法increment = ()=>{const {value} = this.selectNumberthis.props.jia(value*1)}//减法decrement = ()=>{const {value} = this.selectNumberthis.props.jian(value*1)}//奇数再加incrementIfOdd = ()=>{const {value} = this.selectNumberif(this.props.count % 2 !== 0){this.props.jia(value*1)}}//异步加incrementAsync = ()=>{const {value} = this.selectNumberthis.props.jiaAsync(value*1,500)}render() {//console.log('UI组件接收到的props是',this.props);return (<div><h2>我是Count组件,下方组件总人数为:{this.props.renshu}</h2><h4>当前求和为:{this.props.count}</h4><select ref={c => this.selectNumber = c}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select>&nbsp;<button onClick={this.increment}>+</button>&nbsp;<button onClick={this.decrement}>-</button>&nbsp;<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;<button onClick={this.incrementAsync}>异步加</button>&nbsp;</div>)}
}//使用connect()()创建并暴露一个Count的容器组件
export default connect(state => ({count:state.he,renshu:state.rens.length}),{jia:createIncrementAction,jian:createDecrementAction,jiaAsync:createIncrementAsyncAction,}
)(Count)

7. containers/Person/index.jsx

import React, { Component } from 'react'
import {nanoid} from 'nanoid'
import {connect} from 'react-redux'
import {createAddPersonAction} from '../../redux/actions/person'class Person extends Component {addPerson = ()=>{const name = this.nameNode.valueconst age = this.ageNode.valueconst personObj = {id:nanoid(),name,age}this.props.jiaYiRen(personObj)this.nameNode.value = ''this.ageNode.value = ''}render() {return (<div><h2>我是Person组件,上方组件求和为{this.props.he}</h2><input ref={c=>this.nameNode = c} type="text" placeholder="输入名字"/><input ref={c=>this.ageNode = c} type="text" placeholder="输入年龄"/><button onClick={this.addPerson}>添加</button><ul>{this.props.yiduiren.map((p)=>{return <li key={p.id}>{p.name}--{p.age}</li>})}</ul></div>)}
}export default connect(state => ({yiduiren:state.rens,he:state.he}),//映射状态{jiaYiRen:createAddPersonAction}//映射操作状态的方法
)(Person)

7.5 纯函数 -- redux的reducer函数必须是一个纯函数

1.一类特别的函数:只要是同样的输入(实参),必定得到同样的输出(返回)

2. 必须遵守以下一些束缚:

        (1)不得改写参数数据

        (2)不会产生任何副作用,例如网络请求,输入和输出设备

        (3)不能调用Date.now()或者Math.random()等不纯的函数

所以使用return [data,...preState]
而不是perState.unshift(data)等修改原数组的方法
import {ADD_PERSON} from '../constant'//初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]export default function personReducer(preState=initState,action){// console.log('personReducer@#@#@#');const {type,data} = actionswitch (type) {case ADD_PERSON: //若是添加一个人return [data,...preState]default:return preState}
}

7.6 redux开发者工具

(1).安装插件

yarn add redux-devtools-extension

(2).store中进行配置

import {composeWithDevTools} from 'redux-devtools-extension'const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

(3)谷歌浏览器安装插件 -- Redux DevTools

八 项目打包运行

1. 打包

npm run build

2. 准备服务器并测试运行

npm i serve -g
serve build(文件夹根目录)

九 react扩展

1 setState

    (1). setState(stateChange, [callback])------对象式的setState

            1.stateChange为状态改变对象(该对象可以体现出状态的更改)

            2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用 

this.setState({count:count+1},()=>{console.log(this.state.count);
})

    (2). setState(updater, [callback])------函数式的setState

            1.updater为返回stateChange对象的函数。

            2.updater可以接收到state和props。

            4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。

this.setState((state,props) => {return {count:state.count+1}
})

总结:

        1.对象式的setState是函数式的setState的简写方式(语法糖)

        2.使用原则:

                (1).如果新状态不依赖于原状态 ===> 使用对象方式

                (2).如果新状态依赖于原状态 ===> 使用函数方式

                (3).如果需要在setState()执行后获取最新的状态数据,

                    要在第二个callback函数中读取

2 lazyLoad

(1)通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包

 const Login = lazy(()=>import('@/pages/Login'))

   (2)通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面   

 <Suspense fallback={<h1>loading.....</h1>}><Switch><Route path="/xxx" component={Xxxx}/><Redirect to="/login"/></Switch></Suspense>

3 Hooks -- 三个常用的Hook

3.1 State Hook

(1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作

(2). 语法: const [xxx, setXxx] = React.useState(initValue)  

(3). useState()说明:

        参数: 第一次初始化指定的值在内部作缓存

        返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数

(4). setXxx()2种写法:

        setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值

        setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

function Demo(){const [count,setCount] = React.useState(0)const [name,setName] = React.useState('tom')function add(){// setCount(count+1) 第一种写法setCount(count => count+1)}function changeName(){setName('zhangsan')}return(<div><h2>当前求和为:{count}</h2><h2>我的名字是:{name}</h2><button onClick={add}>点我+1</button><button onClick={changeName}>点我改名</button></div>)
}

3.2 Effect Hook

(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)

(2). React中的副作用操作:

        发ajax请求数据获取

        设置订阅 / 启动定时器

        手动更改真实DOM

(3). 语法和说明:

        useEffect(() => {

          // 在此可以执行任何带副作用操作

          return () => { // 在组件卸载前执行

            // 在此做一些收尾工作, 比如清除定时器/取消订阅等

          }

        }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行

   

(4). 可以把 useEffect Hook 看做如下三个函数的组合

        componentDidMount()

        componentDidUpdate()

        componentWillUnmount()

function Demo(){const [count,setCount] = React.useState(0)React.useEffect(()=>{let timer = setInterval(()=>{setCount(count => count+1 )},1000)return ()=>{clearInterval(timer)}},[])function add(){setCount(count => count+1)}//卸载组件的回调function unmount(){ReactDOM.unmountComponentAtNode(document.getElementById('root'))}return(<div><h2>当前求和为:{count}</h2><button onClick={add}>点我+1</button><button onClick={unmount}>卸载组件</button></div>)
}

3.3 Ref Hook

(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据

(2). 语法: const refContainer = useRef()

(3). 作用:保存标签对象,功能与React.createRef()一样

import React from 'react'
import ReactDOM from 'react-dom'//类式组件
/* class Demo extends React.Component {state = {count:0}myRef = React.createRef()add = ()=>{this.setState(state => ({count:state.count+1}))}unmount = ()=>{ReactDOM.unmountComponentAtNode(document.getElementById('root'))}show = ()=>{alert(this.myRef.current.value)}componentDidMount(){this.timer = setInterval(()=>{this.setState( state => ({count:state.count+1}))},1000)}componentWillUnmount(){clearInterval(this.timer)}render() {return (<div><input type="text" ref={this.myRef}/><h2>当前求和为{this.state.count}</h2><button onClick={this.add}>点我+1</button><button onClick={this.unmount}>卸载组件</button><button onClick={this.show}>点击提示数据</button></div>)}
} */function Demo(){//console.log('Demo');const [count,setCount] = React.useState(0)const myRef = React.useRef()React.useEffect(()=>{let timer = setInterval(()=>{setCount(count => count+1 )},1000)return ()=>{clearInterval(timer)}},[])//加的回调function add(){//setCount(count+1) //第一种写法setCount(count => count+1 )}//提示输入的回调function show(){alert(myRef.current.value)}//卸载组件的回调function unmount(){ReactDOM.unmountComponentAtNode(document.getElementById('root'))}return (<div><input type="text" ref={myRef}/><h2>当前求和为:{count}</h2><button onClick={add}>点我+1</button><button onClick={unmount}>卸载组件</button><button onClick={show}>点我提示数据</button></div>)
}export default Demo

4 Fragment

<Fragment><Fragment>

 <></>

import React, { Component,Fragment } from 'react'export default class Demo extends Component {render() {return (<Fragment key={1}><input type="text"/><input type="text"/></Fragment>)}
}
import React, { Component,Fragment } from 'react'export default class Demo extends Component {render() {return (<><input type="text"/><input type="text"/></>)}
}

5 Context

一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

使用:

1. 创建Context容器对象:

 const XxxContext = React.createContext()  

2. 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:   

<xxxContext.Provider value={数据}>子组件</xxxContext.Provider>

3. 后代组件读取数据:

        (1)第一种方式:仅适用于类组件
   

static contextType = xxxContext  // 声明接收contextthis.context // 读取context中的value数据

        (2)第二种方式: 函数组件与类组件都可以  -- 官网使用useContext,以官网为主

 <xxxContext.Consumer>{value => ( // value就是context中的value数据要显示的内容)}</xxxContext.Consumer>

注意:

    在应用开发中一般不用context, 一般都它的封装react插件

import React, { Component } from 'react'
import './index.css'//创建Context对象
const MyContext = React.createContext()
const {Provider,Consumer} = MyContext
export default class A extends Component {state = {username:'tom',age:18}render() {const {username,age} = this.statereturn (<div className="parent"><h3>我是A组件</h3><h4>我的用户名是:{username}</h4><Provider value={{username,age}}><B/></Provider></div>)}
}class B extends Component {render() {return (<div className="child"><h3>我是B组件</h3><C/></div>)}
}/* class C extends Component {//声明接收contextstatic contextType = MyContextrender() {const {username,age} = this.contextreturn (<div className="grand"><h3>我是C组件</h3><h4>我从A组件接收到的用户名:{username},年龄是{age}</h4></div>)}
} */function C(){return (<div className="grand"><h3>我是C组件</h3><h4>我从A组件接收到的用户名:<Consumer>{value => `${value.username},年龄是${value.age}`}</Consumer></h4></div>)
}

6 Component的2个问题

1. 只要执行setState(),即使不改变状态数据, 组件也会重新render() 

2. 只当前组件重新render(), 就会自动重新render子组件 ==> 效率低

 效率高的做法:只有当组件的state或props数据发生改变时才重新render()

原因:Component中的shouldComponentUpdate()总是返回true

 解决:

(1)办法1:

        重写shouldComponentUpdate()方法

        比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false

(2)办法2:  

        使用PureComponent

        PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true

 注意:

        A. 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false  

        B. 不要直接修改state数据, 而是要产生新数据

        C. 项目中一般使用PureComponent来优化

import React, { PureComponent } from 'react'
import './index.css'export default class Parent extends PureComponent {state = {carName:"奔驰c36",stus:['小张','小李','小王']}addStu = ()=>{/* const {stus} = this.statestus.unshift('小刘')this.setState({stus}) */const {stus} = this.statethis.setState({stus:['小刘',...stus]})}changeCar = ()=>{//this.setState({carName:'迈巴赫'})const obj = this.stateobj.carName = '迈巴赫'console.log(obj === this.state);this.setState(obj)}/* shouldComponentUpdate(nextProps,nextState){// console.log(this.props,this.state); //目前的props和state// console.log(nextProps,nextState); //接下要变化的目标props,目标statereturn !this.state.carName === nextState.carName} */render() {console.log('Parent---render');const {carName} = this.statereturn (<div className="parent"><h3>我是Parent组件</h3>{this.state.stus}&nbsp;<span>我的车名字是:{carName}</span><br/><button onClick={this.changeCar}>点我换车</button><button onClick={this.addStu}>添加一个小刘</button><Child carName="奥拓"/></div>)}
}class Child extends PureComponent {/* shouldComponentUpdate(nextProps,nextState){console.log(this.props,this.state); //目前的props和stateconsole.log(nextProps,nextState); //接下要变化的目标props,目标statereturn !this.props.carName === nextProps.carName} */render() {console.log('Child---render');return (<div className="child"><h3>我是Child组件</h3><span>我接到的车是:{this.props.carName}</span></div>)}
}

7 render props

1. 如何向组件内部动态传入带内容的结构(标签)?

        Vue中:

                使用slot技术, 也就是通过组件标签体传入结构  <AA><BB/></AA>

        React中:

                使用children props: 通过组件标签体传入结构

                使用render props: 通过组件标签属性传入结构, 一般用render函数属性

2. children props

<A><B>xxxx</B>
</A>{this.props.children}

问题: 如果B组件需要A组件内的数据, ==> 做不到

3. render props

<A render={(data) => <C data={data}></C>}></A>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data} 
import React, { Component } from 'react'
import './index.css'
import C from '../1_setState'export default class Parent extends Component {render() {return (<div className="parent"><h3>我是Parent组件</h3><A render={(name)=><C name={name}/>}/></div>)}
}class A extends Component {state = {name:'tom'}render() {console.log(this.props);const {name} = this.statereturn (<div className="a"><h3>我是A组件</h3>{this.props.render(name)}</div>)}
}class B extends Component {render() {console.log('B--render');return (<div className="b"><h3>我是B组件,{this.props.name}</h3></div>)}
}

8 错误边界

错误边界:用来捕获后代组件错误,渲染出备用页面

特点:只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件

           在合成事件、定时器中产生的错误。

使用方式:getDerivedStateFromError配合componentDidCatch

// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {console.log(error);// 在render之前触发// 返回新的statereturn {hasError: true,};
}componentDidCatch(error, info) {// 统计页面的错误。发送请求发送到后台去console.log(error, info);
}

9 组件通信方式总结

9.1 方式

props:

        (1).children props

        (2).render props

消息订阅-发布:pubs-sub、event等等

集中式管理:redux、dva等等

conText:生产者-消费者模式

9.2 组件间的关系

父子组件:props

兄弟组件(非嵌套组件):消息订阅-发布、集中式管理

祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(用的少)

十 React Router6

1 概述

1. React Router以三个不同的包发布到npm上,它们分别是:

        1. react-router:路由的核心库,提供了很多的:组件、钩子。

        2. react-router-dom:包含react-router所有内容,并添加一些专门用于DOM的组件,例如:

                <BrowserRouter>等

        3. react-router-native:包括react-router所有内容,并添加一些专门用于ReactNative的Api,              例如:<NativeRouter>等

2. 与React Router5版本相比,改变了什么?

        1. 内置组件的变化:移除<Switch>,新增<Routers>等

        2. 语法的变化:component={About /}变为element={<About/>}等

        3. 新增多个hook:useParams、useNavigate、useMatch等

        4. 官方明确推荐函数式组件

2 Component

1. <BrowerRouter>

2. <HashRouter>

3. <Routes /> 与<Route/>

        1. v6版本中移除了先前的<Switch>,引入了新的替代者<Routes>

        2. <Routes>和<Route>要配合使用,且必须要用<Routes>包裹<Route>

        3. <Route>相当于一个if语句,如果其路径与当前URL匹配,则呈现其对应的组件

        4. <Routes caseSensitive>属性用于指定:匹配时是否区分大小写(默认为false)

        5. 当URL发生变化时,<Routes>都会查看其所有子<Routes>元素

            以找到最佳匹配并呈现组件

        6. <Route>也可以嵌套使用,且可配合useRoutes()配置路由表,

            但需要通过<Outlet>组件来渲染子路由

import React from 'react'
import { NavLink,Routes,Route,Navigate } from 'react-router-dom'
import About from './pages/About'
import Home from './pages/Home'export default function App() {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">{/* 路由导航 */}<NavLink className="list-group-item" to="/about">About</NavLink><NavLink className="list-group-item" to="/home">Home</NavLink></div></div><div className="col-xs-6"><div className="panel"><div className="panel-body">{/* 注册路由 */}<Routes><Route path="/about" element={<About/>} /><Route path="/home" element={<Home/>} /><Route path="/" element={<Navigate to="/about" />} /></Routes></div></div></div></div></div>)
}

4. NavLink高亮效果

1. App.css

.activeClass{background: orange;color: aliceblue;
}

2. app.jsx

function computedClassName({isActive}){return isActive ? 'list-group-item activeClass' : 'list-group-item'
}<NavLink className={computedClassName} to="/about">About</NavLink>
<NavLink className={computedClassName} to="/home">Home</NavLink>

5 useRoutes路由表使用

1. 新建routes文件夹,新建index.js

import About from '../pages/About'
import Home from '../pages/Home'
import {Navigate} from 'react-router-dom'const routes = [{path:'/about',element:<About/>},{path:'/home',element:<Home/>},{path:'/',element:<Navigate to="/about"/>}
]export default routes

2. app.jsx使用

import React from 'react'
import { NavLink,useRoutes } from 'react-router-dom'
import routes from './routes'export default function App() {// 根据路由表生成对应的路由规则const element = useRoutes(routes)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">{/* 路由导航 */}<NavLink className="list-group-item" to="/about">About</NavLink><NavLink className="list-group-item" to="/home">Home</NavLink></div></div><div className="col-xs-6"><div className="panel"><div className="panel-body">{element}</div></div></div></div></div>)
}

6 嵌套路由

1. routes/index.js

import About from '../pages/About'
import Home from '../pages/Home'
import Message from '../pages/Message'
import News from '../pages/News'
import { Navigate } from 'react-router-dom'const routes = [{path: '/about',element: <About />},{path: '/home',element: <Home />,children: [{path: 'news',element: <News />,},{path: 'message',element: <Message />,}]},{path: '/',element: <Navigate to="/about" />}
]export default routes

2.Home.jsx

import React from 'react'
import { NavLink,Outlet } from 'react-router-dom'
export default function Home() {return (<div><h2>Home组件内容</h2><div><ul className="nav nav-tabs"><li><NavLink className="list-group-item" to="/home/news">News</NavLink></li><li><NavLink className="list-group-item" to="/home/message">Message</NavLink></li></ul>{/* 指定路由组件呈现的位置 */}<Outlet/></div></div>)
}

7 路由Params传参

1. routes/index.js占位

{path: 'message',element: <Message />,children: [{path: 'detail/:id/:title/:content',element: <Detail />,}]}

2. Message.jsx传值

<Link to={`detail/${m.id}/${m.title}/${m.content}`}>{m.title}</Link>

3. Detail.jsx接收参数 -- useParams

import React from 'react'
import { useParams, useMatch } from 'react-router-dom'export default function Detail() {const { id, title, content } = useParams()// const x = useMatch('/home/message/detail/:id/:title/:content')return (<ul><li>消息编号:{id}</li><li>消息标题:{title}</li><li>消息内容:{content}</li></ul>)
}

8 路由search传参

1. message.jsx传递参数

<Link to={`detail?id=${m.id}&title=${m.title}&content=${m.content}`}>{m.title}</Link>

2. Detail.jsx接收参数

import React from 'react'
import { useSearchParams,useLocation } from 'react-router-dom'export default function Detail() {const [search,setSearch] = useSearchParams()const id = search.get('id')const title = search.get('title')const content = search.get('content')const x = useLocation ()console.log(x)return (<ul><li><button onClick={()=>{setSearch('id=005&title=哈哈&content=嘻嘻')}}>点我更新收到的search参数</button></li><li>消息编号:{id}</li><li>消息标题:{title}</li><li>消息内容:{content}</li></ul>)
}

9 路由state传参

1. Message.jsx传参

<Link to="detail"state={{id:m.id,title:m.title,content:m.content}}
>{m.title}</Link>

2.Detail.jsx接收参数

import React from 'react'
import { useLocation } from 'react-router-dom'
export default function Detail() {const {state} = useLocation()return (<ul><li>消息编号:{state.id}</li><li>消息标题:{state.title}</li><li>消息内容:{state.content}</li></ul>)
}

10 编程式路由导航 -- useNavigate

1.使用

import React, { useState } from 'react'
import{Link,Outlet} from 'react-router-dom'
import { useNavigate } from 'react-router-dom'export default function Message() {const [messages] = useState([{ id: '001', title: '消息1', content: '消息1的内容' },{ id: '002', title: '消息2', content: '消息2的内容' },{ id: '003', title: '消息3', content: '消息3的内容' },{ id: '004', title: '消息4', content: '消息4的内容' }])const navigate = useNavigate()function showDetail(m){navigate('detail',{replace:false,state:{id:m.id,title:m.title,content:m.content}})}return (<div><ul>{messages.map((m) => {return (<li key={m.id}><Link to="detail"state={{id:m.id,title:m.title,content:m.content}}>{m.title}</Link><button onClick={()=>{showDetail(m)}}>查看详情</button></li>)})}</ul><hr/><Outlet /></div>)
}

2. 前进后退

import React from 'react'
import { useNavigate } from 'react-router-dom'export default function Header() {const navigate = useNavigate()function back(){navigate(-1)}function forward(){navigate(1)}return (<div className="col-xs-offset-2 col-xs-8"><div className="page-header"><h2>React Router Demo</h2></div><button onClick={back}>后退</button><button onClick={forward}>前进</button></div>)
}

11 useInRouterContext()

作用:如果组件在<Router>的上下文中呈现,则useInRouterContext钩子返回true,否则返回false

12 useNavigationType()

1. 作用:返回当前的导航类型(用户是如何来到当前页面的)

2. 返回值:POP、PUSH、REPLACE

3. 备注:POP是指在浏览器中直接打开了这个路由的文件(刷新页面) 

13 useOutlet()

1. 作用:用来呈现当前组件中渲染的嵌套路由

const result = useOutlet()
console.log(result)//如果嵌套的路由没有挂载,则result为null
//如果嵌套的路由已经挂载,则展示嵌套的路由对象

14 useResolvePath()

作用:给定一个URL值,解析其中的:path、search、hash值


http://chatgpt.dhexx.cn/article/ttX5jtk1.shtml

相关文章

react全家桶实战(千峰教育)

说明&#xff1a;本笔记为本人基于千锋教育2022版React全家桶教程_react零基础入门到项目实战完整版的学习笔记&#xff0c;知识点不清或不全&#xff0c;可以到视频教程中学习 文章目录 一、安装create-react-app&#xff0c;新建react项目1、安装脚手架create-react-app<s…

搭建react全家桶

1.安装react项目 ①使用webpack安装 npx create-react-app my-app cd my-app npm start ②使用vite安装 创建Vite项目 npm create vitelatest my-vue-app --template react cd my-app npm run dev 配置vite.config.js import { defineConfig } from vite import react from vit…

React 全家桶

文章目录 前言一、React是什么&#xff1f;二、基础内容1. React 相关 js 库2. React 开发者调试工具3. JSX语法规则4. 模块与组件、模块化与组件化5. 类的基本知识 三、React 面向组件编程1. 函数式组件2. 类式组件3. 组件实例的三个核心属性&#xff1a; state、refs、props-…

React全家桶

文章目录 第1章&#xff1a;React入门1.1. React简介1.1.1. 官网1.1.2. 介绍描述1.1.3. React的特点1.1.4. React高效的原因 1.2. React的基本使用1.2.1. 效果1.2.2. 相关js库1.2.3. 创建虚拟DOM的两种方式1.2.4. 虚拟DOM与真实DOM 1.3. React JSX1.3.1. 效果1.3.2. JSX1.3.3. …

react全家桶有哪些?(详细)

一 、 create-react-app脚手架 对于现在比较流行的三大框架都有属于自己的脚手架&#xff08;目前这些脚手架都是使用node编写的&#xff0c;并且都是基于webpack的&#xff09;&#xff1a; Vue的脚手架&#xff1a;vue-cliAngular的脚手架&#xff1a;angular-cliReact的脚手…

React全家桶详细讲解-图文并茂

文章目录 前言一、React-表单处理受控组件使用步骤多表单元素优化非受控组件 二、React-组件综合案例需求分析搭建任务的模板渲染列表添加任务删除任务 三、React-组件进阶组件通讯介绍组件的props 四、react-组件通讯的三种方式react-父组件传递数据给子组件react-子组件传递数…

【尚硅谷React】——React全家桶笔记

文章目录 第1章 React简介1.1 React的特点1.2 引入文件1.3 JSX1.3.1 为什么要用JSX1.3.2 JSX语法规则 1.4 虚拟DOM1.5 模块与组件1.5.1 模块1.5.2 组件 第2章 React面向组件编程2.1 创建组件2.1.1 函数式组件2.1.2 类式组件 2.2 组件实例的三大属性2.2.1 state属性2.2.2 props属…

React全家桶(收藏吃灰必备!)

文章目录 ECMAScript61、ES6简介1.1、什么是ES61.2、ECMAScript和JavaScript的关系1.3、为什么要学习ES6&#xff1f; 2、ES6环境搭建2.1、前期准备2.2、ES6环境搭建 3、let与const3.1、let命令3.2、const命令 4、ES6解构赋值4.1、解构赋值概述4.2、解构模型4.3、数组的解构赋值…

Neurosynth元分析——认知解码工具,软件包安装以及使用

Neurosynth元分析——认知解码工具,软件包安装以及使用 NeuroSynth 基本简介基本原理例子Neurosynth package安装及使用创建虚拟环境安装Dependencies:安装neurosynthNeurosynth使用加载必要的包下载neurosynth数据参考如上图所示。NeuroSynth 元分析感兴趣的区域沿功能连接梯…

编码和解码

概念 字符的三种形态 图片来自&#xff1a;https://zhuanlan.zhihu.com/p/25435644 编码&#xff1a;将字符转为字节序列&#xff08;abcdefg-------------> 0101010…10010&#xff09; 解码&#xff1a;将字节序列转为字符&#xff08;1001010…10010110------> abcd…

编码器和解码器

1.编码器—解码器&#xff08;seq2seq&#xff09; 编码器的作用是把一个不定长的输入序列变换成一个定长的背景变量c&#xff0c;并在该背景变量中编码输入序列信息。常用的编码器是循环神经网络。 编码器可以是一个单向的循环神经网络&#xff0c;每个时间步的隐藏状态只取决…

二维码解码工具

http://tool.chinaz.com/qrcode/?jdfwkeyweexu

编解码工具

支持各种文件摘要&#xff08;Hash&#xff09;、Base64编码、Hex编码、国密sm2加密、Rsa加密&#xff0c;以及各种对称加密算法的小工具 下载地址&#xff1a;https://pan.baidu.com/s/1AJwUaVizzk5HeLa_8Q5AqA 提取码&#xff1a;4567

编码与解码

什么是编码与解码 电脑是由电路板组成&#xff0c;电路板里面集成了无数的电阻和电容&#xff0c; 交流电经过电容的时候&#xff0c;电压比较低 记为低电平 &#xff0c; 用0表示&#xff0c;交流电流过电阻的时候&#xff0c;电压比较高&#xff0c;记为高电平&#xff0c;用…

Encoder编码器、Decoder解码器

知乎用户对编码器解码器的理解 Encoder&#xff1a; 本身其实就是一连串的卷积网络。该网络主要由卷积层&#xff0c;池化层和BatchNormalization层组成。卷积层负责获取图像局域特征&#xff0c;池化层对图像进行下采样并且将尺度不变特征传送到下一层&#xff0c;而BN主要对…

Protobuf在线解码工具推荐

P1: CyberChef 地址&#xff1a;CyberChef 非常灵活&#xff0c;支持各种导入格式可以直接转成json支持导入.proto文件 P2: protobuf-decoder 地址&#xff1a;protobuf-decoder 有对齐问题 P3: protogen 地址&#xff1a;protogen 有乱码问题 P4: PB-JCE-Decoder&…

在线JWT Token解析解码工具

1&#xff1a;JWT Token在线解析解码 - ToolTT在线工具箱 2&#xff1a; 3&#xff1a;

密码解码常用工具网站汇总

xssee:http://web2hack.org/xssee xssee:http://evilcos.me/lab/xssee 在线编码解码(多种并排):http://bianma.911cha.com 在线加密解密(多种):http://encode.chahuo.com Unicode转中文:http://www.bejson.com/convert/unicode_chinese 栅栏密码 && 凯撒密码 &&…

URL在线编码/解码工具

一刀工具箱提供在线URL编码解码工具:对网址Url进行UrlEncode编码转换,UrlEncode编码,UrlDecode解码。 代码片段 methods:{convertEncode(){this.item encodeURIComponent(this.value)},convertDecode(){this.item decodeURIComponent(this.value);}} URL在线编码/解码工具 …

URL 编码和解码工具

简介 本文简要介绍一款编解码工具&#xff0c;它支持URL编码和解码、Base64编码和解码。 官网地址&#xff1a;https://smart-tools.cn/dev/encoder/app 背景 URL编码 URL编码是浏览器发送数据给服务器时使用的编码。它是编码算法&#xff0c;而不是加密算法。其目的是把任…