第1章:React入门
React简介
官网
1. 英文官网: https://reactjs.org/
2. 中文官网: https://react.docschina.org/
介绍描述
1. 用于动态构建用户界面的 JavaScript 库(只关注于视图)
2. 由Facebook开源
React的特点
1. 声明式编码
2. 组件化编码
3. React Native 可以使用于移动端开发
4. 高效(优秀的Diffing算法)
React高效的原因
1. 使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。
2. DOM Diffing算法, 最小化页面重绘。
React的基本使用
效果

<!DOCTYPE html> <html lang="en">   <head>     <meta charset="UTF-8">     <title>hello_react</title>   </head>   <body>          <div id="test"></div>
           <script type="text/javascript" src="../js/react.development.js"></script>          <script type="text/javascript" src="../js/react-dom.development.js"></script>          <script type="text/javascript" src="../js/babel.min.js"></script>
      <script type="text/babel" >               const VDOM = <h1>Hello,React</h1>               ReactDOM.render(VDOM,document.getElementById('test'))     </script>   </body> </html>
   | 
 
相关js库
- react.js:React核心库。
2. react-dom.js:提供操作DOM的react扩展库。
3. babel.min.js:解析JSX语法代码转为JS代码的库。
 
创建虚拟DOM的两种方式

1. 纯JS方式(一般不用,比如写标签嵌套比较麻烦)
比如下面创建一个h1标签里面再创建一个span标签
<!DOCTYPE html> <html lang="en">   <head>     <meta charset="UTF-8">     <title>2_使用js创建虚拟DOM</title>   </head>   <body>          <div id="test"></div>
           <script type="text/javascript" src="../js/react.development.js"></script>          <script type="text/javascript" src="../js/react-dom.development.js"></script>
      <script type="text/javascript" >               const VDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React'))              ReactDOM.render(VDOM,document.getElementById('test'))     </script>   </body> </html>
   | 
 
- JSX方式
 
<!DOCTYPE html> <html lang="en">   <head>     <meta charset="UTF-8">     <title>1_使用jsx创建虚拟DOM</title>   </head>   <body>          <div id="test"></div>
           <script type="text/javascript" src="../js/react.development.js"></script>          <script type="text/javascript" src="../js/react-dom.development.js"></script>          <script type="text/javascript" src="../js/babel.min.js"></script>
      <script type="text/babel" >               const VDOM = (           <h1 id="title">       		/* 这里就可以很容易实现标签嵌套了 */        	 <span>Hello,React</span>       	</h1>       )              ReactDOM.render(VDOM,document.getElementById('test'))     </script>   </body> </html>
   | 
 
虚拟DOM与真实DOM
- React提供了一些API来创建一种 “特别” 的一般js对象
** const VDOM = React.createElement('xx',{id:'xx'},'xx')**
 上面创建的就是一个简单的虚拟DOM对象
2. 虚拟DOM对象(上面我们在jsx里面写的标签就是虚拟DOM)最终都会被React转换为真实的DOM
3. 我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新。
关于虚拟DOM:
1.本质是Object类型的对象(一般对象)
2.虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
3.虚拟DOM最终会被React转化为真实DOM,呈现在页面上。
 
React JSX
效果

JSX
- 全称:  JavaScript XML
2. react定义的一种类似于XML的JS扩展语法: JS + XML本质是**React.createElement(component, props, ...children)**方法的语法糖
3. 作用: 用来简化创建虚拟DOM 
1) 写法:**var ele = <h1>Hello JSX!</h1>**
2) 注意1:它不是字符串, 也不是HTML/XML标签
3) 注意2:它最终产生的就是一个JS对象
4. 标签名任意: HTML标签或其它标签
5. 标签属性任意: HTML标签属性或其它属性
6. 基本语法规则
1) 遇到 <开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析
2) 遇到以 { 开头的代码,以JS语法解析: 标签中的js表达式必须用{ }包含
7. babel.js的作用
1) 浏览器不能直接解析JSX代码, 需要babel转译JSX为纯JS的代码才能运行
2) 只要用了JSX,都要加上type=”text/babel”, 声明需要babel来处理 
jsx语法规则:
  1.定义虚拟DOM时,不要写引号。
  2.标签中混入JS表达式时要用{}。
  3.样式的类名指定不要用class,要用className。
4.内联样式,要用style={{key:value}}的形式去写。<br />
  | 
 
  5.只有一个根标签
  6.标签必须闭合
  7.标签首字母
    (1).若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。(小写字母开头说明就是普通的html标签)
    (2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。(大写字母开头就说明是自己自定义的组件)
<!DOCTYPE html> <html lang="en">   <head>     <meta charset="UTF-8">     <title>jsx语法规则</title>     <style>       .title{         background-color: orange;         width: 200px;       }     </style>   </head>   <body>          <div id="test"></div>
           <script type="text/javascript" src="../js/react.development.js"></script>          <script type="text/javascript" src="../js/react-dom.development.js"></script>          <script type="text/javascript" src="../js/babel.min.js"></script>
      <script type="text/babel" >       const myId = 'aTgUiGu'       const myData = 'HeLlo,rEaCt'       //1.创建虚拟DOM       const VDOM = (       <div>         <h2 className="title" id={myId.toLowerCase()}>
          	<span style={{color:'white',fontSize:'29px'}}>{myData.toLowerCase()}</span>      	  </h2>         <h2 className="title" id={myId.toUpperCase()}>           <span style={{color:'white',fontSize:'29px'}}>{myData.toLowerCase()}</span>         </h2>         <input type="text"/>     	</div>       )       //2.渲染虚拟DOM到页面       ReactDOM.render(VDOM,document.getElementById('test'))     </script>   </body> </html>
   | 
 
渲染虚拟DOM(元素)
- 语法:  
**ReactDOM.render(virtualDOM, containerDOM)**
2. 作用: 将虚拟DOM元素渲染到页面中的真实容器DOM中显示
3. 参数说明
1) 参数一: 纯js或jsx(一般都是jsx)创建的虚拟dom对象
2) 参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是html页面中的一个div) 
一定注意区分:【js语句(代码)】与【js表达式】
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式:
  (1). a
  (2). a+b
  (3). demo(1)
  (4). arr.map() 
  (5). function test () {}
2.语句(代码):
下面这些都是语句(代码):
  (1).if(){}
  (2).for(){}
  	(3).switch(){case:xxxx}
JSX练习
需求: 动态展示如下列表
<!DOCTYPE html> <html lang="en">   <head>     <meta charset="UTF-8">     <title>jsx小练习</title>   </head>   <body>          <div id="test"></div>
           <script type="text/javascript" src="../js/react.development.js"></script>          <script type="text/javascript" src="../js/react-dom.development.js"></script>          <script type="text/javascript" src="../js/babel.min.js"></script>
      <script type="text/babel" >              const data = ['Angular','React','Vue']              const VDOM = (       <div>         <h1>前端js框架列表</h1>         <ul>           { 						             data.map((item,index)=>{             	return <li key={index}>{item}</li>             })           }       	</ul>       </div>       )              ReactDOM.render(VDOM,document.getElementById('test'))     </script>   </body> </html>
   | 
 
模块与组件、模块化与组件化的理解
模块
- 理解:向外提供特定功能的js程序, 一般就是一个js文件
2. 为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂。
3. 作用:复用js, 简化js的编写, 提高js运行效率
 
组件
- 理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)
2. 为什么要用组件: 一个界面的功能更复杂
3. 作用:复用编码, 简化项目编码, 提高运行效率
 
模块化
当应用的js都以模块来编写的, 这个应用就是一个模块化的应用
组件化
当应用是以多组件的方式实现, 这个应用就是一个组件化的应用
第2章:React面向组件编程
2.1. 基本理解和使用
2.1.1. 使用React开发者工具调试

2.1.2. 效果

创建组件的两种方法
函数式组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>1_函数式组件</title> </head> <body>
  <div id="test"></div>
 
  <script type="text/javascript" src="../js/react.development.js"></script>
  <script type="text/javascript" src="../js/react-dom.development.js"></script>
  <script type="text/javascript" src="../js/babel.min.js"></script>
  <script type="text/babel">      function MyComponent(){     console.log(this);      return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>   }      ReactDOM.render(<MyComponent/>,document.getElementById('test')) </script> </body> </html>
   | 
 
执行了ReactDOM.render(…….之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
类式组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>2_类式组件</title> </head> <body>
  <div id="test"></div>
 
  <script type="text/javascript" src="../js/react.development.js"></script>
  <script type="text/javascript" src="../js/react-dom.development.js"></script>
  <script type="text/javascript" src="../js/babel.min.js"></script>
  <script type="text/babel">
  class MyComponent extends React.Component {   render(){               console.log('render中的this:',this);     return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>   } }      ReactDOM.render(<MyComponent/>,document.getElementById('test')) </script> </body> </html>
   | 
 
执行了ReactDOM.render(…….之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出来该类的实例(React帮我们new),并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
2.1.3. 注意
- 组件名必须首字母大写
2. 虚拟DOM元素只能有一个根元素
3. 虚拟DOM元素必须有结束标签
 
2.1.4. 渲染类组件标签的基本流程
- React内部会创建组件实例对象
2. 调用render()得到虚拟DOM, 并解析为真实DOM
3. 插入到指定的页面元素内部
 
2.2. 组件实例(也就是类组件,不是函数组件)的三大核心属性1: state
2.2.1. 效果
需求: 定义一个展示天气信息的组件
1. 默认展示天气炎热或凉爽
2. 点击文字切换天气

**2.2.2. 理解 **
- state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
2. 组件被称为”状态机”, 通过更新组件的state来更新对应的页面显示(重新渲染组件)
 
2.2.3. 强烈注意
- 组件中render方法中的this为组件实例对象
2. 组件自定义的方法中this为undefined,如何解决?
a) 强制绑定this: 通过函数对象的bind()
b) 箭头函数
3. 状态数据,不能直接修改或更新(需要调用相关的方法,setState) 
State基础写法
<!DOCTYPE html> <html lang="en">
    <head>     <meta charset="UTF-8">     <title>state</title>   </head>
    <body>          <div id="test"></div>
           <script type="text/javascript" src="../js/react.development.js"></script>          <script type="text/javascript" src="../js/react-dom.development.js"></script>          <script type="text/javascript" src="../js/babel.min.js"></script>
      <script type="text/babel">              class Weather extends React.Component {
                   constructor(props) {           console.log('constructor');           super(props)                      this.state = { isHot: false, wind: '微风' }                      this.changeWeather = this.changeWeather.bind(this)         }                     render() {           console.log('render');                      console.log(this)                      const { isHot, wind } = this.state           return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>         }                              changeWeather() {                                                 console.log('changeWeather');                      const isHot = this.state.isHot                                 this.setState({ isHot: !isHot })           console.log(this);                                    }       }              ReactDOM.render(<Weather />, document.getElementById('test'))
      </script>   </body>
  </html>
   | 
 
state的简写方式
<!DOCTYPE html> <html lang="en">
    <head>     <meta charset="UTF-8">     <title>state简写方式</title>   </head>
    <body>          <div id="test"></div>
           <script type="text/javascript" src="../js/react.development.js"></script>          <script type="text/javascript" src="../js/react-dom.development.js"></script>          <script type="text/javascript" src="../js/babel.min.js"></script>
      <script type="text/babel">              class Weather extends React.Component {                           state = { isHot: false, wind: '微风' }            render() {           const { isHot, wind } = this.state           return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>         }                                       changeWeather = () => {           const isHot = this.state.isHot           this.setState({ isHot: !isHot })         }       }              ReactDOM.render(<Weather />, document.getElementById('test'))
      </script>   </body>
  </html>
   | 
 
2.3. 组件三大核心属性2: props
2.3.1. 效果
需求: 自定义用来显示一个人员信息的组件
1. 姓名必须指定,且为字符串类型;
2. 性别为字符串类型,如果性别没有指定,默认为男
3. 年龄为字符串类型,且为数字类型,默认值为18

2.3.2. 理解
- 每个组件对象都会有props(properties的简写)属性
2. 组件标签的所有属性都保存在props中
 
2.3.3. 作用
- 通过标签属性从组件外向组件内(父子传递)传递变化的数据
2. 注意: 组件内部(子组件不能修改props数据,也就是说props是只读的单向数据流)不要修改props数据
 
2.3.4. 编码操作
- 内部读取某个属性值
**this**.**props**.**name**
2. 对props中的属性值进行类型限制和必要性限制
第一种方式(React v15.5 开始已弃用):
_Person_.**propTypes **= {<br /> **name**: **React**.**PropTypes**.**string**.isRequired,<br /> **age**: **React**.**PropTypes**.**number**<br />}
第二种方式(新):使用prop-types库进限制(需要引入prop-types库,因为大部分情况下我们都不需要进行类型限制,所以React就没帮我们引入)
_Person_.**propTypes **= {<br />  **name**: **PropTypes**.**string**.isRequired,<br />  **age**: **PropTypes**.**number**. <br />}
3. 扩展属性: 将对象的所有属性通过props传递(这也是js的扩展运算符语法。对于对象的扩展运算符,会把属性名作为等号左边,属性值作为等号右边,不过我觉得没啥用,因为可以直接传一个对象过去。)<**Person **{...**_person_**}/>
4. 默认属性值:
Person.**defaultProps **= {<br />  **age**: 18,<br />  **sex**:**'男'<br />**}
5. 组件类的构造函数
**constructor**(props){<br />  **super**(props)<br />  **console**.log(props)_//打印所有属性<br />_} 
props的基本使用
<!DOCTYPE html> <html lang="en">
  <head> 	<meta charset="UTF-8"> 	<title>props基本使用</title> </head>
  <body> 	 	<div id="test1"></div> 	<div id="test2"></div> 	<div id="test3"></div>
  	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel"> 		 		class Person extends React.Component { 			render() { 				 				const { name, age, sex } = this.props 				return ( 					<ul> 						<li>姓名:{name}</li> 						<li>性别:{sex}</li> 						<li>年龄:{age + 1}</li> 					</ul> 				) 			} 		} 		 		ReactDOM.render(<Person name="jerry" age={19} sex="男" />, 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')) 	</script> </body>
  </html>
   | 
 
或者直接传递一个对象到子组件,就不用用展开运算符了
<!DOCTYPE html> <html lang="en">
  <head>   <meta charset="UTF-8">   <title>props基本使用</title> </head>
  <body>      <div id="test1"></div>
 
       <script type="text/javascript" src="./js/react.development.js"></script>      <script type="text/javascript" src="./js/react-dom.development.js"></script>      <script type="text/javascript" src="./js/babel.min.js"></script>
    <script type="text/babel">          class Person extends React.Component {       render() {         console.log(this.props);         console.log(this.props.p.name)         const { name, age, sex } = this.props.p         return (           <ul>             <li>姓名:{name}</li>             <li>性别:{sex}</li>             <li>年龄:{age + 1}</li>           </ul>         )       }     }     const p = { name: '老刘', age: 18, sex: '女' }          ReactDOM.render(<Person p={p} />, document.getElementById('test1'))   </script> </body>
  </html>
   | 
 
对props进行限制(类型,是否必传)
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>对props进行限制</title> </head> <body> 	 	<div id="test1"></div> 	<div id="test2"></div> 	<div id="test3"></div> 	 	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script> 	 	<script type="text/javascript" src="../js/prop-types.js"></script>
  	<script type="text/babel"> 		 		class Person extends React.Component{ 			render(){ 				 				const {name,age,sex} = this.props 				 				 				return ( 					<ul> 						<li>姓名:{name}</li> 						<li>性别:{sex}</li> 						<li>年龄:{age+1}</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={100} 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('我说话了'); 		} 	</script> </body> </html>
   | 
 
props的简写方式
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>对props进行限制</title> </head> <body> 	 	<div id="test1"></div> 	<div id="test2"></div> 	<div id="test3"></div> 	 	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script> 	 	<script type="text/javascript" src="../js/prop-types.js"></script>
  	<script type="text/babel"> 		 		class Person extends React.Component{
  			constructor(props){ 				 				 				super(props) 				console.log('constructor',this.props); 			}
  			 			static propTypes = { 				name:PropTypes.string.isRequired,  				sex:PropTypes.string, 				age:PropTypes.number, 			}
  			 			static defaultProps = { 				sex:'男', 				age:18  			} 			 			render(){ 				 				const {name,age,sex} = this.props 				 				 				return ( 					<ul> 						<li>姓名:{name}</li> 						<li>性别:{sex}</li> 						<li>年龄:{age+1}</li> 					</ul> 				) 			} 		}
  		 		ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1')) 	</script> </body> </html>
   | 
 
函数组件使用props
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>对props进行限制</title> </head> <body> 	 	<div id="test1"></div> 	<div id="test2"></div> 	<div id="test3"></div> 	 	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script> 	 	<script type="text/javascript" src="../js/prop-types.js"></script>
  	<script type="text/babel"> 		 		function Person (props){ 			const {name,age,sex} = props 			return ( 					<ul> 						<li>姓名:{name}</li> 						<li>性别:{sex}</li> 						<li>年龄:{age}</li> 					</ul> 				) 		} 		Person.propTypes = { 			name:PropTypes.string.isRequired,  			sex:PropTypes.string, 			age:PropTypes.number, 		}
  		 		Person.defaultProps = { 			sex:'男', 			age:18  		} 		 		ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1')) 	</script> </body> </html>
   | 
 
2.4. 组件三大核心属性3: refs与事件处理
2.4.1. 效果
需求: 自定义组件, 功能说明如下:
1. 点击按钮, 提示第一个输入框中的值
2. 当第2个输入框失去焦点时, 提示这个输入框中的值
效果如下:

2.4.2. 理解
组件内的标签可以定义ref属性来标识自己
2.4.3. 编码
- 字符串形式的ref
**<input ref="input1"/>**
2. 回调形式的ref**<input ref={(c)=>{this.input1 = c}}/>**
3. createRef创建ref容器
myRef = React.createRef() 
 
2.4.4 示例
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>1_字符串形式的ref</title> </head> <body> 	 	<div id="test"></div> 	 	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel"> 		 		class Demo extends React.Component{ 			 			showData = ()=>{          				const {input1} = this.refs          				alert(input1.value) 			} 			 			showData2 = ()=>{ 				const {input2} = this.refs 				alert(input2.value) 			} 			render(){ 				return( 					<div> 						<input ref="input1" type="text" placeholder="点击按钮提示数据"/>              //给虚拟dom绑定点击事件是onClick,然后值是一个函数,注意只需要函数名就可以了 						<button onClick={this.showData}>点我提示左侧的数据</button>  						<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/> 					</div> 				) 			} 		} 		 		ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test')) 	</script> </body> </html>
   | 
 
<!DOCTYPE html> <html lang="en">
  <head>   <meta charset="UTF-8">   <title>1_字符串形式的ref</title> </head>
  <body>      <div id="test"></div>
       <script type="text/javascript" src="../js/react.development.js"></script>      <script type="text/javascript" src="../js/react-dom.development.js"></script>      <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/babel">          class Demo extends React.Component {              showData = () => {         const { input1 } = this         alert(input1.value)       }              showData2 = () => {         const { input2 } = this         alert(input2.value)       }       render() {         return (           <div>             // 下面这一行中的ref中的回调函数会被react默认调用,也就是默认赋值,之后就可以通过实例对象的input1属性进行操作document对象了             //这个参数传的c就是这个标签的虚拟dom对象,然后再给这个实例对象身上的input1属性进行赋值             <input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据" />              <button onClick={this.showData}>点我提示左侧的数据</button>              <input onBlur={this.showData2} ref={c => this.input2 = c} type="text" placeholder="失去焦点提示数据" />            </div>         )       }     }          ReactDOM.render(<Demo a="1" b="2" />, document.getElementById('test'))   </script> </body>
  </html>
   | 
 
<!DOCTYPE html> <html lang="en">
  <head> 	<meta charset="UTF-8"> 	<title>3_回调ref中回调执行次数的问题</title> </head>
  <body> 	 	<div id="test"></div>
  	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel"> 		 		class Demo extends React.Component {
  			state = { isHot: false }
  			showInfo = () => { 				const { input1 } = this 				alert(input1.value) 			}
  			changeWeather = () => { 				 				const { isHot } = this.state 				 				this.setState({ isHot: !isHot }) 			}
  			 			saveInput = (c) => { 				this.input1 = c; 				console.log('@', c); 			}
  			render() { 				const { isHot } = this.state 				return ( 					<div> 						<h2>今天天气很{isHot ? '炎热' : '凉爽'}</h2> 						{/*以内联函数的方式进行,这样的话如果发生修改操作的话会调用两次函数,第一次参数为null,第二次才为实际传的参数 								<input ref={(c)=>{this.input1 = c;console.log('@',c);}} type="text"/><br/><br/>*/}
  						{/*下面这个是定义成class的绑定函数,就是在class里面写一个函数进行调用*/} 						<input ref={this.saveInput} type="text" /><br /><br /> 						<button onClick={this.showInfo}>点我提示输入的数据</button> 						<button onClick={this.changeWeather}>点我切换天气</button> 					</div> 				) 			} 		} 		 		ReactDOM.render(<Demo />, document.getElementById('test')) 	</script> </body>
  </html>
   | 
 
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>4_createRef</title> </head> <body> 	 	<div id="test"></div> 	 	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel"> 		 		class Demo extends React.Component{ 			 			myRef = React.createRef() 			myRef2 = React.createRef() 			 			showData = ()=>{ 				alert(this.myRef.current.value); 			} 			 			showData2 = ()=>{ 				alert(this.myRef2.current.value); 			} 			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> 				) 			} 		} 		 		ReactDOM.render(<Demo/>,document.getElementById('test')) 	</script> </body> </html>
   | 
 
2.4.5. 事件处理
- 通过onXxx属性指定事件处理函数(比如点击事件、失去焦点事件等等)(注意大小写)
1) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件—————— 为了更好的兼容性
2) React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)————————为了的高效
2. 通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref,可以直接使用event就好辣 
<!DOCTYPE html> <html lang="en">
  <head> 	<meta charset="UTF-8"> 	<title>事件处理</title> </head>
  <body> 	 	<div id="test"></div>
  	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel"> 		 		class Demo extends React.Component { 			 			myRef = React.createRef() 			myRef2 = React.createRef()
  			showData = (event) => { 				console.log(event.target); 				alert(this.myRef.current.value); 			}
  			showData2 = (event) => { 				 				 				alert(event.target.value); 			}
  			render() { 				return ( 					<div> 						<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />  						<button onClick={this.showData}>点我提示左侧的数据</button>  						<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据" />  					</div> 				) 			} 		} 		 		ReactDOM.render(<Demo />, document.getElementById('test')) 	</script> </body>
  </html>
   | 
 
2.5. 收集表单数据
2.5.1. 效果
需求: 定义一个包含表单的组件
输入用户名密码后, 点击登录提示输入信息

2.5.2. 理解
包含表单的组件分类
1. 受控组件(vue里面的双向数据绑定)
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>2_受控组件</title> </head> <body> 	 	<div id="test"></div> 	 	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel"> 		 		class Login extends React.Component{
  			 			state = { 				username:'',  				password:''  			}
  			 			saveUsername = (event)=>{ 				this.setState({username:event.target.value}) 			}
  			 			savePassword = (event)=>{ 				this.setState({password:event.target.value}) 			}
  			 			handleSubmit = (event)=>{ 				event.preventDefault()  				const {username,password} = this.state 				alert(`你输入的用户名是:${username},你输入的密码是:${password}`) 			}
  			render(){ 				return( 					<form onSubmit={this.handleSubmit}> 						用户名:<input onChange={this.saveUsername} type="text" name="username"/> 						密码:<input onChange={this.savePassword} type="password" name="password"/> 						<button>登录</button> 					</form> 				) 			} 		} 		 		ReactDOM.render(<Login/>,document.getElementById('test')) 	</script> </body> </html>
   | 
 
- 非受控组件
 
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>1_非受控组件</title> </head> <body> 	 	<div id="test"></div> 	 	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel"> 		 		class Login extends React.Component{ 			handleSubmit = (event)=>{ 				event.preventDefault()  				const {username,password} = this 				alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`) 			} 			render(){ 				return( 					<form onSubmit={this.handleSubmit}> 						用户名:<input ref={c => this.username = c} type="text" name="username"/> 						密码:<input ref={c => this.password = c} type="password" name="password"/> 						<button>登录</button> 					</form> 				) 			} 		} 		 		ReactDOM.render(<Login/>,document.getElementById('test')) 	</script> </body> </html>
   | 
 
高阶函数和函数的柯里化
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
        1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
        2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
        常见的高阶函数有:Promise、setTimeout、arr.map()等等
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。 
 function sum(a){
    return(b)=>{
      return (c)=>{
        return a+b+c
      }
    }
  } 
<!DOCTYPE html> <html lang="en">
  <head> 	<meta charset="UTF-8"> 	<title>高阶函数_函数柯里化</title> </head>
  <body> 	 	<div id="test"></div>
  	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel">
  		 		class Login extends React.Component { 			 			state = { 				username: '',  				password: ''  			}
  			 			saveFormData = (dataType) => { 				return (event) => { 					 					
 
 
 
 
  					this.setState({ [dataType]: event.target.value }) 				} 			}
  			 			handleSubmit = (event) => { 				event.preventDefault()  				const { username, password } = this.state 				alert(`你输入的用户名是:${username},你输入的密码是:${password}`) 			} 			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(<Login />, document.getElementById('test')) 	</script> </body>
  </html>
   | 
 
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>2_不用函数柯里化的实现</title> </head> <body> 	 	<div id="test"></div> 	 	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel"> 		 		class Login extends React.Component{ 			 			state = { 				username:'',  				password:''  			}
  			 			saveFormData = (dataType,event)=>{ 				this.setState({[dataType]:event.target.value}) 			}
  			 			handleSubmit = (event)=>{ 				event.preventDefault()  				const {username,password} = this.state 				alert(`你输入的用户名是:${username},你输入的密码是:${password}`) 			} 			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(<Login/>,document.getElementById('test')) 	</script> </body> </html>
   | 
 
2.6. 组件的生命周期
2.6.1. 效果
需求:定义组件实现以下功能:
1. 让指定的文本做显示 / 隐藏的渐变动画
2. 从完全可见,到彻底消失,耗时2S
3. 点击“不活了”按钮从界面中卸载组件

<!DOCTYPE html> <html lang="en">
  <head> 	<meta charset="UTF-8"> 	<title>1_引出生命周期</title> </head>
  <body> 	 	<div id="test"></div>
  	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel"> 		 		 		class Life extends React.Component {
  			state = { opacity: 1 }
  			death = () => { 				 				ReactDOM.unmountComponentAtNode(document.getElementById('test')) 			}
  			 			componentDidMount() { 				console.log('componentDidMount'); 				this.timer = setInterval(() => { 					 					let { opacity } = this.state 					 					opacity -= 0.1 					if (opacity <= 0) opacity = 1 					 					this.setState({ opacity }) 				}, 200); 			}
  			 			componentWillUnmount() { 				 				clearInterval(this.timer) 			}
  			 			render() { 				console.log('render'); 				return ( 					<div> 						<h2 style={{ opacity: this.state.opacity }}>React学不会怎么办?</h2> 						<button onClick={this.death}>不活了</button> 					</div> 				) 			} 		} 		 		ReactDOM.render(<Life />, document.getElementById('test')) 	</script> </body>
  </html>
   | 
 
2.6.2. 理解
- 组件从创建到死亡它会经历一些特定的阶段。
2. React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
3. 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
 
2.6.3. 生命周期流程图(旧)

生命周期的三个阶段(旧)
**	1. 初始化阶段:** 由ReactDOM.render()触发—初次渲染
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount()
**	2. 更新阶段: **由组件内部this.setSate()或父组件重新render触发
1. shouldComponentUpdate()    组件是否应该更新的阀门  返回true为放行,为false就不往下执行了(默认返回为真)
2. componentWillUpdate()    组件将要更新(强制更新,这里可以不更改state就强制更新)
3. render()
4. componentDidUpdate()   更新完毕的钩子
	**3. 卸载组件: **由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount()
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>2_react生命周期(旧)</title> </head> <body> 	 	<div id="test"></div> 	 	 	<script type="text/javascript" src="../js/react.development.js"></script> 	 	<script type="text/javascript" src="../js/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/babel.min.js"></script>
  	<script type="text/babel"> 		 		class Count extends React.Component{
  			 			constructor(props){ 				console.log('Count---constructor'); 				super(props) 				 				this.state = {count:0} 			}
  			 			add = ()=>{ 				 				const {count} = this.state 				 				this.setState({count:count+1}) 			}
  			 			death = ()=>{ 				ReactDOM.unmountComponentAtNode(document.getElementById('test')) 			}
  			 			force = ()=>{ 				this.forceUpdate() 			}
  			 			componentWillMount(){ 				console.log('Count---componentWillMount'); 			}
  			 			componentDidMount(){ 				console.log('Count---componentDidMount'); 			}
  			 			componentWillUnmount(){ 				console.log('Count---componentWillUnmount'); 			}
  			 			shouldComponentUpdate(){ 				console.log('Count---shouldComponentUpdate'); 				return true 			}
  			 			componentWillUpdate(){ 				console.log('Count---componentWillUpdate'); 			}
  			 			componentDidUpdate(){ 				console.log('Count---componentDidUpdate'); 			}
  			render(){ 				console.log('Count---render'); 				const {count} = this.state 				return( 					<div> 						<h2>当前求和为:{count}</h2> 						<button onClick={this.add}>点我+1</button> 						<button onClick={this.death}>卸载组件</button> 						<button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button> 					</div> 				) 			} 		} 		 		 		class A extends React.Component{ 			 			state = {carName:'奔驰'}
  			changeCar = ()=>{ 				this.setState({carName:'奥拓'}) 			}
  			render(){ 				return( 					<div> 						<div>我是A组件</div> 						<button onClick={this.changeCar}>换车</button> 						<B carName={this.state.carName}/> 					</div> 				) 			} 		} 		 		 		class B extends React.Component{ 			        			componentWillReceiveProps(props){ 				console.log('B---componentWillReceiveProps',props); 			}
  			 			shouldComponentUpdate(){ 				console.log('B---shouldComponentUpdate'); 				return true 			} 			 			componentWillUpdate(){ 				console.log('B---componentWillUpdate'); 			}
  			 			componentDidUpdate(){ 				console.log('B---componentDidUpdate'); 			}
  			render(){ 				console.log('B---render'); 				return( 					<div>我是B组件,接收到的车是:{this.props.carName}</div> 				) 			} 		} 		 		 		ReactDOM.render(<Count/>,document.getElementById('test')) 	</script> </body> </html>
   | 
 
2.6.4. 生命周期流程图(新)
旧版生命周期流程图

新版生命周期流程图

生命周期的三个阶段(新)
1. 初始化阶段: 由ReactDOM.render()触发—初次渲染
1. constructor()
2. getDerivedStateFromProps               当state的值在任何时候都取决于props的时候才用这个钩子
3. render()
4. componentDidMount()
**	2. 更新阶段: **由组件内部this.setSate()或父组件重新render触发
1. getDerivedStateFromProps
2. shouldComponentUpdate()
3. render()
4. getSnapshotBeforeUpdate    快照值可以是任何类型的值
5. componentDidUpdate()   这里函数里面可以有两个参数,一个preProps,一个preState 
	**3. 卸载组件: **由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount()
2.6.4.1 具体实例
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>3_react生命周期(新)</title> </head> <body> 	 	<div id="test"></div> 	 	 	<script type="text/javascript" src="../js/17.0.1/react.development.js"></script> 	 	<script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>
  	<script type="text/babel"> 		 		class Count extends React.Component{ 			 			constructor(props){ 				console.log('Count---constructor'); 				super(props) 				 				this.state = {count:0} 			}
  			 			add = ()=>{ 				 				const {count} = this.state 				 				this.setState({count:count+1}) 			}
  			 			death = ()=>{ 				ReactDOM.unmountComponentAtNode(document.getElementById('test')) 			}
  			 			force = ()=>{ 				this.forceUpdate() 			} 			 			 			static getDerivedStateFromProps(props,state){ 				console.log('getDerivedStateFromProps',props,state); 				return null 			}
  			 			getSnapshotBeforeUpdate(){ 				console.log('getSnapshotBeforeUpdate'); 				return 'atguigu' 			}
  			 			componentDidMount(){ 				console.log('Count---componentDidMount'); 			}
  			 			componentWillUnmount(){ 				console.log('Count---componentWillUnmount'); 			}
  			 			shouldComponentUpdate(){ 				console.log('Count---shouldComponentUpdate'); 				return true 			}
  			 			componentDidUpdate(preProps,preState,snapshotValue){ 				console.log('Count---componentDidUpdate',preProps,preState,snapshotValue); 			} 			 			render(){ 				console.log('Count---render'); 				const {count} = this.state 				return( 					<div> 						<h2>当前求和为:{count}</h2> 						<button onClick={this.add}>点我+1</button> 						<button onClick={this.death}>卸载组件</button> 						<button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button> 					</div> 				) 			} 		} 		 		 		ReactDOM.render(<Count count={199}/>,document.getElementById('test')) 	</script> </body> </html>
   | 
 
<!DOCTYPE html> <html lang="en"> <head> 	<meta charset="UTF-8"> 	<title>4_getSnapShotBeforeUpdate的使用场景</title> 	<style> 		.list{ 			width: 200px; 			height: 150px; 			background-color: skyblue; 			overflow: auto; 		} 		.news{ 			height: 30px; 		} 	</style> </head> <body> 	 	<div id="test"></div> 	 	 	<script type="text/javascript" src="../js/17.0.1/react.development.js"></script> 	 	<script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script> 	 	<script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>
  	<script type="text/babel"> 		class NewsList extends React.Component{
  			state = {newsArr:[]}
  			componentDidMount(){ 				setInterval(() => { 					 					const {newsArr} = this.state 					 					const news = '新闻'+ (newsArr.length+1) 					 					this.setState({newsArr:[news,...newsArr]}) 				}, 1000); 			}
  			getSnapshotBeforeUpdate(){ 				return this.refs.list.scrollHeight 			}
  			componentDidUpdate(preProps,preState,height){ 				this.refs.list.scrollTop += this.refs.list.scrollHeight - height 			}
  			render(){ 				return( 					<div className="list" ref="list"> 						{ 							this.state.newsArr.map((n,index)=>{ 								return <div key={index} className="news">{n}</div> 							}) 						} 					</div> 				) 			} 		} 		ReactDOM.render(<NewsList/>,document.getElementById('test')) 	</script> </body> </html>
   | 
 
2.6.5. 重要的勾子
- render:初始化渲染或更新渲染调用
2. componentDidMount:开启监听, 发送ajax请求
3. componentWillUnmount:做一些收尾工作, 如: 清理定时器
 
2.6.6. 即将废弃的勾子
- componentWillMount
2. componentWillReceiveProps
3. componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。虽然这三个基本都不用
 
2.7. 虚拟DOM与DOM Diffing算法
2.7.1. 效果
需求:验证虚拟DOM Diffing算法的存在

我们可以看见,上面的图片是span标签内部一直在更新,diff算法的最小粒度是标签,所以最好把内容都写在标签里面,也就是说时间改变了,前面的It is也是跟着改变的,但是前面的输入框和Hello不会进行改变,因为根本就没变,如果输入框也改变的话输入框的内容会被清空
2.7.2. 基本原理图
经典面试题:
1). react/vue中的key有什么作用?(key的内部原理是什么?)
2). 为什么遍历列表时,key最好不要用index?
1. 虚拟DOM中key的作用:
  1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用
  2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】, 
                随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
      a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
            (1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
            (2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
      b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
            根据数据创建新的真实DOM,随后渲染到到页面
2. 用index作为key可能会引发的问题:
  1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
          会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
  2. 如果结构中还包含输入类的DOM:
          会产生错误DOM更新 ==> 界面有问题。
  3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
            仅用于渲染列表用于展示,使用index作为key是没有问题的。
3. 开发中如何选择key?:
  1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
  2.如果确定只是简单的展示数据,用index也是可以的。

验证
<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>key的作用</title> </head> <body> <div id="test"></div>
  <script type="text/javascript" src="../js/react.development.js"></script>
  <script type="text/javascript" src="../js/react-dom.development.js"></script>
  <script type="text/javascript" src="../js/babel.min.js"></script>
  <script type="text/babel"> 	 	/*  		慢动作回放----使用index索引值作为key
  			初始数据: 					{id:1,name:'小张',age:18}, 					{id:2,name:'小李',age:19}, 			初始的虚拟DOM: 					<li key=0>小张---18<input type="text"/></li> 					<li key=1>小李---19<input type="text"/></li>
  			更新后的数据: 					{id:3,name:'小王',age:20}, 					{id:1,name:'小张',age:18}, 					{id:2,name:'小李',age:19}, 			更新数据后的虚拟DOM: 					<li key=0>小王---20<input type="text"/></li> 					<li key=1>小张---18<input type="text"/></li> 					<li key=2>小李---19<input type="text"/></li>
  	-----------------------------------------------------------------
  	慢动作回放----使用id唯一标识作为key
  			初始数据: 					{id:1,name:'小张',age:18}, 					{id:2,name:'小李',age:19}, 			初始的虚拟DOM: 					<li key=1>小张---18<input type="text"/></li> 					<li key=2>小李---19<input type="text"/></li>
  			更新后的数据: 					{id:3,name:'小王',age:20}, 					{id:1,name:'小张',age:18}, 					{id:2,name:'小李',age:19}, 			更新数据后的虚拟DOM: 					<li key=3>小王---20<input type="text"/></li> 					<li key=1>小张---18<input type="text"/></li> 					<li key=2>小李---19<input type="text"/></li>
 
  	 */ 	class Person extends React.Component{
  		state = { 			persons:[ 				{id:1,name:'小张',age:18}, 				{id:2,name:'小李',age:19}, 			] 		}
  		add = ()=>{ 			const {persons} = this.state 			const p = {id:persons.length+1,name:'小王',age:20} 			this.setState({persons:[p,...persons]}) 		}
  		render(){ 			return ( 				<div> 					<h2>展示人员信息</h2> 					<button onClick={this.add}>添加一个小王</button> 					<h3>使用index(索引值)作为key</h3> 					<ul> 						{ 							this.state.persons.map((personObj,index)=>{ 								return <li key={index}>{personObj.name}---{personObj.age}<input type="text"/></li> 							}) 						} 					</ul> 					<hr/> 					<hr/> 					<h3>使用id(数据的唯一标识)作为key</h3> 					<ul> 						{ 							this.state.persons.map((personObj)=>{ 								return <li key={personObj.id}>{personObj.name}---{personObj.age}<input type="text"/></li> 							}) 						} 					</ul> 				</div> 			) 		} 	}
  	ReactDOM.render(<Person/>,document.getElementById('test')) </script> </body> </html>
 
   | 
 
第3章:React应用(基于React脚手架)
3.1. 使用create-react-app创建react应用
3.1.1. react脚手架
- xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
1. 包含了所有需要的配置(语法检查、jsx编译、devServer…)
2. 下载好了所有相关的依赖
3. 可以直接运行一个简单效果
2. react提供了一个用于创建react项目的脚手架库: create-react-app
3. 项目的整体技术架构为:  react + webpack + es6 + eslint
4. 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化
 
3.1.2. 创建项目并启动
第一步,全局安装:npm i -g create-react-app
第二步,切换到想创项目的目录,使用命令:create-react-app hello-react
第三步,进入项目文件夹:cd hello-react
第四步,启动项目:npm start
3.1.3. react脚手架项目结构
public ---- 静态资源文件夹<br />		favicon.icon ------ 网站页签图标<br />		**index.html -------- 主页面**<br />		logo192.png ------- logo图<br />		logo512.png ------- logo图<br />		manifest.json ----- 应用加壳(套壳ios和Android)的配置文件<br />		robots.txt -------- 爬虫协议文件<br />src ---- 源码文件夹<br />		App.css -------- App组件的样式<br />		**App.js --------- App组件**<br />		App.test.js ---- 用于给App做测试<br />		index.css ------ 样式<br />		**index.js ------- 入口文件(区别于vue,vue的是main.js)**<br />		logo.svg ------- logo图<br />		reportWebVitals.js<br />			--- 页面性能分析文件(需要web-vitals库的支持)<br />		setupTests.js<br />			---- 组件单元测试的文件(需要jest-dom库的支持)<br />需要注意的是,组件的名称和Vue的一样,首字母大写,并且每个组件需要遵守相应书写的格式<br />编写组件的快捷键是类组件rcc。函数组件rfc
3.1.4. 功能界面的组件化编码流程(通用)
- 拆分组件: 拆分界面,抽取组件
2. 实现静态组件: 使用组件实现静态页面效果
3. 实现动态组件
3.1 动态显示初始化数据
3.1.1 数据类型
3.1.2 数据名称
3.1.2 保存在哪个组件?
3.2 交互(从绑定事件监听开始)
 
**3.2. 组件的组合使用-TodoList **
功能: 组件化实现此功能
1. 显示所有todo列表
2. 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本

    1.拆分组件、实现静态组件,注意:className、style的写法
    2.动态初始化列表,如何确定将数据放在哪个组件的state中?
          ——某个组件使用:放在其自身的state中
          ——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
    3.关于父子之间通信:
        1.【父组件】给【子组件】传递数据:通过props传递
        2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
    4.注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value
    5.状态在哪里,操作状态的方法就在哪里
第4章:React ajax
4.1. 理解
4.1.1. 前置说明
- React本身只关注于界面, 并不包含发送ajax请求的代码
2. 前端应用需要通过ajax请求与后台进行交互(json数据)
3. react应用中需要集成第三方ajax库(或自己封装)
 
4.1.2. 常用的ajax请求库
- jQuery: 比较重, 如果需要另外引入不建议使用
2. axios: 轻量级, 建议使用
1) 封装XmlHttpRequest对象的ajax
2)  promise风格
3) 可以用在浏览器端和node服务器端
 
react脚手架配置代理总结
方法一
在package.json中追加如下配置
"proxy":"http://localhost:5000"
   | 
 
说明:
- 优点:配置简单,前端请求资源时可以不加任何前缀。
 
- 缺点:不能配置多个代理。
 
- 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
 
- 此外,所有的请求本来需要5000端口的数据的,现在需要给3000(自己开前端的服务端口)端口发。也相当于给自己的代理服务器发请求,让代理服务器去发请求
 

方法二
- 第一步:创建代理配置文件 (这个配置文件会由React加载,所以名称不能变)
 
在src下创建配置文件:src/setupProxy.js
   | 
 
- 编写setupProxy.js配置具体代理规则:
 
const proxy = require('http-proxy-middleware')
  module.exports = function(app) {   app.use(     proxy('/api1', {         target: 'http://localhost:5000',        changeOrigin: true,        
 
 
 
        pathRewrite: {'^/api1': ''}      }),     proxy('/api2', {          target: 'http://localhost:5001',       changeOrigin: true,       pathRewrite: {'^/api2': ''}     })   ) }
  | 
 
说明:
- 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
 
- 缺点:配置繁琐,前端请求资源时必须加前缀。
 
4.2. axios
4.2.1. 文档
https://github.com/axios/axios
4.2.2. 相关API
GET请求
axios.get('/user?ID=12345')   .then(function (response) {     console.log(response.data);   })   .catch(function (error) {     console.log(error);   });
  axios.get('/user', {   params: {     ID: 12345   } })   .then(function (response) {     console.log(response);   })   .catch(function (error) {     console.log(error);   });
 
  | 
 
POST请求
axios.post('/user', {   firstName: 'Fred',   lastName: 'Flintstone' })   .then(function (response) {     console.log(response);   })   .catch(function (error) {     console.log(error);   });
  | 
 
4.3. 案例—github用户搜索
4.3.1. 效果

请求地址: https://api.github.com/search/users?q=xxxxxx
4.4. 消息订阅-发布机制
这个东西适用于任何组件的沟通!!!
1. 工具库: PubSubJS
2. 下载:  npm install pubsub-js –save   
yarn add pubsub-js –save
3. 使用: 
1) import PubSub from ‘pubsub-js’ //引入,这个引入是在组件中引入。
2) PubSub.subscribe(‘delete’, function(data){ }); //订阅
3) PubSub.publish(‘delete’, data) //发布消息
具体代码
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> 		) 	} }
 
  | 
 
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 		 		PubSub.publish('atguigu',{isFirst:false,isLoading:true}) 		 		axios.get(`/api1/search/users?q=${keyWord}`).then( 			response => { 				 				PubSub.publish('atguigu',{isLoading:false,users:response.data.items}) 			}, 			error => { 				 				PubSub.publish('atguigu',{isLoading:false,err:error.message}) 			} 		) 	}
  	render() { 		return ( 			<section className="jumbotron"> 				<h3 className="jumbotron-heading">搜索github用户</h3> 				<div> 					<input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>  					<button onClick={this.search}>搜索</button> 				</div> 			</section> 		) 	} }
 
  | 
 
import React, { Component } from 'react' import PubSub from 'pubsub-js' import './index.css'
  export default class List extends Component {
  	state = {  		users:[],  		isFirst:true,  		isLoading:false, 		err:'', 	} 
  	componentDidMount(){ 		this.token = PubSub.subscribe('atguigu',(_,stateObj)=>{ 			this.setState(stateObj) 		}) 	}
  	componentWillUnmount(){ 		PubSub.unsubscribe(this.token) 	}
  	render() { 		const {users,isFirst,isLoading,err} = this.state 		return ( 			<div className="row"> 				{ 					isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> : 					isLoading ? <h2>Loading......</h2> : 					err ? <h2 style={{color:'red'}}>{err}</h2> : 					users.map((userObj)=>{ 						return ( 							<div key={userObj.id} className="card"> 								<a rel="noreferrer" href={userObj.html_url} target="_blank"> 									<img alt="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/> 								</a> 								<p className="card-text">{userObj.login}</p> 							</div> 						) 					}) 				} 			</div> 		) 	} }
 
  | 
 
4.5. 扩展:Fetch

4.5.1. 文档
- https://github.github.io/fetch/
2. https://segmentfault.com/a/1190000003810652
 
4.5.2. 特点
- fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求
2. 老版本浏览器可能不支持
 
4.5.3. 相关API
- GET请求
 
fetch(url).then(function(response) {      return response.json() }).then(function(data) {      console.log(data) }).catch(function(e) {   console.log(e) });
  | 
 
- POST请求
 
fetch(url, {   method: "POST",   body: JSON.stringify(data), }).then(function(data) {   console.log(data) }).catch(function(e) {   console.log(e) })
  | 
 
github搜索案例相关知识点
1.设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。
2.ES6小知识点:解构赋值+重命名
let obj = {a:{b:1}}
const {a} = obj; //传统解构赋值
const {a:{b}} = obj; //连续解构赋值
const {a:{b:value}} = obj; //连续解构赋值+重命名
3.消息订阅与发布机制
	1.先订阅,再发布(理解:有一种隔空对话的感觉)
	2.适用于任意组件间通信
	3.要在组件的componentWillUnmount中取消订阅
	4.fetch发送请求(关注分离的设计思想)
try {   const response= await fetch(`/api1/search/users2?q=${keyWord}`)   const data = await response.json()   console.log(data); } catch (error) {   console.log('请求出错',error); }
  | 
 
第5章:React路由
5.1. 相关理解
5.1.1. SPA的理解
- 单页Web应用(single page web application,SPA)。
2. 整个应用只有一个完整的页面。
3. 点击页面中的链接不会刷新页面,只会做页面的局部更新。
4. 数据都需要通过ajax请求获取, 并在前端异步展现。
 
5.1.2. 路由的理解
1. 什么是路由?
1. 一个路由就是一个映射关系(key:value)
2. key为路径, value可能是function或component
2. 路由分类
1. 后端路由:
1) 理解: value是function, 用来处理客户端提交的请求。
2) 注册路由:router.get(path, function(req, res))
3) 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
2. 前端路由:
1) 浏览器端路由,value是component,用于展示页面内容。
2) 注册路由: 
3) 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件
history的结构是栈的结构,如果push会压栈,回去是弹栈,如果是replace是会替换栈顶的那一条数据
5.1.3. react-router-dom的理解
- react的一个插件库。
2. 专门用来实现一个SPA应用。
3. 基于react的项目基本都会用到此库。
 
5.2. react-router-dom相关API
这个是5版本的,后面有6版本的
5.2.1. 内置组件
2. 
3. 
4. 
5. 
6. 
7. 
 
5.2.2. 其它
- history对象
2. match对象
3. withRouter函数
 
5.3. 基本路由使用
5.3.1. 效果

5.3.2. 准备
- 下载react-router-dom: npm install –save react-router-dom@5
2. 引入bootstrap.css:  
总结
  1.明确好界面中的导航区、展示区
  2.导航区的a标签改为Link标签
        <Link to="/xxxxx">Demo</Link>
  3.展示区写Route标签进行路径的匹配
        <Route path='/xxxx' component={Demo}/>
  4.的最外侧包裹了一个或
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="/about" component={About}/>                 <Route path="/home" component={Home}/>               </div>             </div>           </div>         </div>       </div>     )   } }
 
  | 
 
 import React from 'react'
  import ReactDOM from 'react-dom'
  import {BrowserRouter} from 'react-router-dom'
  import App from './App'
  ReactDOM.render(      <BrowserRouter>   <App/>   </BrowserRouter>, document.getElementById('root') )
 
  | 
 
路由组件与一般组件
1.写法不同:
	一般组件:
	路由组件:
2.存放位置不同:
	一般组件:components文件夹下
	路由组件:pages文件夹下
3.接收到的props不同:
	一般组件:写组件标签时传递了什么,就能收到什么
	路由组件:程序员没有机会进行传递,由react固定给我们传递,所以会接收到三个固定的属性
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"
   | 
 
封装NavLink
import React, { Component } from 'react' import {Route} from 'react-router-dom' import Home from './pages/Home'  import About from './pages/About'  import Header from './components/Header'  import MyNavLink from './components/MyNavLink'
  export default class App extends Component { 	render() { 		return ( 			<div> 				<div className="row"> 					<div className="col-xs-offset-2 col-xs-8"> 						<Header/> 					</div> 				</div> 				<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中靠路由链接实现切换组件--编写路由链接 */} 							{/* 标签体内的About会传入子组件的props属性中,属性名称叫children */} 							<MyNavLink to="/about">About</MyNavLink> 							<MyNavLink to="/home">Home</MyNavLink> 						</div> 					</div> 					<div className="col-xs-6"> 						<div className="panel"> 							<div className="panel-body"> 								{/* 注册路由 */} 								<Route path="/about" component={About}/> 								<Route path="/home" component={Home}/> 							</div> 						</div> 					</div> 				</div> 			</div> 		) 	} }
 
  | 
 
import React, { Component } from 'react' import {NavLink} from 'react-router-dom'
  export default class MyNavLink extends Component { 	render() { 		 		return ( 			 			 			 			 			 			<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/> 		) 	} }
 
  | 
 
NavLink 与封装 NavLink
    1.NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
Switch的使用
通常状况下,path和component是一一对应的关系,但是如果我们不配置switch的话,会出现以下问题
<div className="panel-body">   {}      <Route path="/about" component={About}/>   <Route path="/home" component={Home}/>   <Route path="/home" component={Test}/> </div>
   | 
 
但是如果配置了Switch的话
<div className="panel-body">   {}      <Switch>     <Route path="/about" component={About}/>     <Route path="/home" component={Home}/>     <Route path="/home" component={Test}/>   </Switch> </div>
   | 
 
解决多级路径刷新页面样式丢失的问题
1.public/index.html 中 引入样式时不写 ./ 写 / (常用)
2.public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)
3.使用HashRouter,因为锚点后面的就不算路径了
<div className="panel-body">   {}        <Route path="/haohao/about" component={About}/>     <Route path="/haohao/home" component={Home}/> </div>
   | 
 
shift+刷新=强制刷新
路由的严格匹配和模糊匹配
1.默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
2.开启严格匹配:<Route exact={true} path="/about" component={About}/>
3.严格匹配不要随便开启,需要的时候再开,有些时候开启会导致无法继续匹配二级路由
举例
模糊匹配:给多了可以,给少了不行,默认开启这个
<div className="row">   <div className="col-xs-2 col-xs-offset-2">     <div className="list-group">       <MyNavLink to="/about">About</MyNavLink>       <MyNavLink to="/home/a/b">Home</MyNavLink>     </div>   </div>   <div className="col-xs-6">     <div className="panel">       <div className="panel-body">         {/* 注册路由 */}         <Switch>           {/* 这里其实是模糊匹配,下面的home是可以匹配上的 */}           <Route  path="/about" component={About}/>           <Route  path="/home" component={Home}/>         </Switch>       </div>     </div>   </div> </div>
   | 
 
精准匹配:主打的就是一个多了不行少了也不行,但是尽量不要用这个
<div className="row">   <div className="col-xs-2 col-xs-offset-2">     <div className="list-group">       <MyNavLink to="/about">About</MyNavLink>       <MyNavLink to="/home/a/b">Home</MyNavLink>     </div>   </div>   <div className="col-xs-6">     <div className="panel">       <div className="panel-body">         {/* 注册路由 */}         <Switch>           {/* 这里开启了严格匹配,下面的home是匹配不到的 */}           <Route exact path="/about" component={About}/>           <Route exact path="/home" component={Home}/>         </Switch>       </div>     </div>   </div> </div>
   | 
 
Redirect的使用
1.一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
2.具体编码:
<Switch>   <Route path="/about" component={About}/>   <Route path="/home" component={Home}/>   <Redirect to="/about"/> </Switch>
   | 
 
3.完整代码:
import React, { Component } from 'react'
  import {Route,Switch,Redirect} from 'react-router-dom' import Home from './pages/Home'  import About from './pages/About'  import Header from './components/Header'  import MyNavLink from './components/MyNavLink'
  export default class App extends Component { 	render() { 		return (       <div>         <div className="row">           <div className="col-xs-offset-2 col-xs-8">             <Header />           </div>         </div>         <div className="row">           <div className="col-xs-2 col-xs-offset-2">             <div className="list-group">               <MyNavLink to="/about">About</MyNavLink>               <MyNavLink to="/home">Home</MyNavLink>             </div>           </div>           <div className="col-xs-6">             <div className="panel">               <div className="panel-body">                 {/* 注册路由 */}                 <Switch>                   <Route path="/about" component={About} />                   <Route path="/home" component={Home} />                   {/* Redirect相当于一个兜底的人,如果访问的路径都匹配不上,就访问about */}                   <Redirect to="/about" />                 </Switch>               </div>             </div>           </div>         </div>       </div>     ); 	} }
 
  | 
 
5.4. 嵌套路由使用
注意注意!嵌套路由不能使用严格匹配,开启会无法导致二级路由
   1.注册子路由时要写上父路由的path值
   2.路由的匹配是按照注册路由的顺序进行的
效果

代码
一级路由是/home
二级路由是/home/news和/home/message
import React, { Component } from "react"; import MyNavLink from "../../components/MyNavLink"; import { Route, Switch, Redirect } from "react-router-dom"; import News from "./News"; import Message from "./Message";
  export default class 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>     );   } }
 
  | 
 
5.5. 向路由组件传递参数数据
向路由组件传递params参数
1.params参数
      路由链接(携带参数):<Link to=’/demo/test/tom/18’}>详情
      注册路由(声明接收):
      接收参数:this.props.match.params

所以是三级路由并且根据三级路由进行传递数据
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参数 */}         {/* 下面这个参数可以在子组件中以props的形式接收,props直接给封装好了 */}         <Route path="/home/message/detail/:id/:title" component={Detail} />       </div>     );   } }
 
  | 
 
import React, { Component } from "react";
  const DetailData = [   { id: "01", content: "你好,中国" },   { id: "02", content: "你好,尚硅谷" },   { id: "03", content: "你好,未来的自己" }, ]; export default class Detail extends Component {   render() {     console.log(this.props);          const { id, title } = this.props.match.params;     const findResult = DetailData.find((detailObj) => {       return detailObj.id === id;     });     return (       <ul>         <li>ID:{id}</li>         <li>TITLE:{title}</li>         <li>CONTENT:{findResult.content}</li>       </ul>     );   } }
 
  | 
 
向路由组件传递search参数

路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>
注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
接收参数:this.props.location.search 
备注:获取到的 search 是 urlencoded 编码字符串(key=value&key=value),需要借助 querystring 解析
具体代码:
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}>                 {/* 向路由组件传递search参数 */}                 {/* 这里的search参数会落在子组件的props属性的location下的search属性下,所以叫search属性不叫query属性 */}                 <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);          const { search } = this.props.location;          const { id, title } = qs.parse(search.slice(1));     const findResult = DetailData.find((detailObj) => {       return detailObj.id === id;     });     return (       <ul>         <li>ID:{id}</li>         <li>TITLE:{title}</li>         <li>CONTENT:{findResult.content}</li>       </ul>     );   } }
 
  | 
 
向路由组件传递state参数
这个state和组件的state不一样
**这个state是存储在history的,这说明我们如果清理历史记录的话,再刷新页面就看不到这个state了  **
之前那两个就算是清除历史记录数据也不会丢,因为又通过url进行传过去了

- 路由链接(携带参数):
<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link> 
- 注册路由(无需声明,正常注册即可):
<Route path="/demo/test" component={Test}/> 
- 接收参数:
this.props.location.state 
- 备注:刷新也可以保留住参数,但是清楚历史就保不住了
 
具体代码:
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}>                 {/* 向路由组件传递state参数 */}                 {/* 这里通过对象进行传输数据,state可以在子组件的location中的state中获取                  pathname之前没写是因为他是默认属性,spring里面也有这种情况*/}                 <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>     );   } }
 
  | 
 
import React, { Component } from 'react'
 
  const DetailData = [ 	{id:'01',content:'你好,中国'}, 	{id:'02',content:'你好,尚硅谷'}, 	{id:'03',content:'你好,未来的自己'} ] export default class Detail extends Component { 	render() { 		console.log(this.props); 		 		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> 		) 	} }
 
  | 
 
5.6. 多种路由跳转方式
push和replace
开启replace模式,替换push模式
<MyNavLink replace to="/about">About</MyNavLink>
编程式导航
借助 this.prosp.history 对象上的 API 对操作路由跳转、前进、后退(但是这也意味着非路由组件就用不了这个API了)
- -this.prosp.history.push()
 
- -this.prosp.history.replace()
 
- -this.prosp.history.goBack()
 
- -this.prosp.history.goForward()
 
- -this.prosp.history.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'}, 		] 	}
  	replaceShow = (id,title)=>{ 		 		
  		 		
  		 		this.props.history.replace(`/home/message/detail`,{id,title}) 	}
  	pushShow = (id,title)=>{ 		 		
  		 		
  		 		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.state 		return ( 			<div> 				<ul> 					{ 						messageArr.map((msgObj)=>{ 							return ( 								<li key={msgObj.id}>
  									{/* 向路由组件传递params参数 */} 									{/* <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}
  									{/* 向路由组件传递search参数 */} 									{/* <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}
  									{/* 向路由组件传递state参数 */} 									<Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
  									 <button onClick={()=> this.pushShow(msgObj.id,msgObj.title)}>push查看</button> 									 <button onClick={()=> this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button> 								</li> 							) 						}) 					} 				</ul> 				<hr/> 				{/* 声明接收params参数 */} 				{/* <Route path="/home/message/detail/:id/:title" component={Detail}/> */}
  				{/* search参数无需声明接收,正常注册路由即可 */} 				{/* <Route path="/home/message/detail" component={Detail}/> */}
  				{/* state参数无需声明接收,正常注册路由即可 */} 				<Route path="/home/message/detail" component={Detail}/>
  				<button onClick={this.back}>回退</button>  				<button onClick={this.forward}>前进</button>  				<button onClick={this.go}>go</button>
  			</div> 		) 	} }
 
  | 
 
WithRouter
withRouter就可以使一般组件用上路由组件的API
withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
withRouter的返回值是一个新组件,里面有路由组件中含有的props属性。之后就可以像路由组件一样操作了
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>          <button onClick={this.forward}>前进</button>          <button onClick={this.go}>go</button>       </div>     );   } }
 
 
  export default withRouter(Header);
 
  | 
 
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可以用于解决一些路径错误相关的问题。
第6章:React UI组件库
6.1.流行的开源React UI组件库
6.1.1. material-ui(国外)
- 官网: http://www.material-ui.com/#/
2. github: https://github.com/callemall/material-ui
 
6.1.2. ant-design(国内蚂蚁金服)
- 官网: https://ant.design/index-cn
2. Github: https://github.com/ant-design/ant-design/ 
antd 的按需引入+自定义主题
1.安装依赖:yarn add react-app-rewired customize-cra babel-plugin-import less less-loader
2.修改package.json
"scripts": {   "start": "react-app-rewired start",     "build": "react-app-rewired build",     "test": "react-app-rewired test",     "eject": "react-scripts eject" },
  | 
 
3.根目录下创建config-overrides.js
 const { override, fixBabelImports,addLessLoader} = require('customize-cra'); module.exports = override(   fixBabelImports('import', {     libraryName: 'antd',     libraryDirectory: 'es',     style: true,   }),      addLessLoader({     lessOptions:{       javascriptEnabled: true,       modifyVars: { '@primary-color': 'green' },     }   }), );
 
  | 
 
4.备注:不用在组件里亲自引入样式了,即:import ‘antd/dist/antd.css’应该删掉
但是这些推荐查文档,但是文档可能会有些落后,到时候就只能查看lessLoader的文档了
第7章:redux
其实对标的就是Vue的vuex和pinia
7.1. redux理解
7.1.1. 学习文档
- 英文文档: https://redux.js.org/
2. 中文文档: http://www.redux.org.cn/
3. Github: https://github.com/reactjs/redux
 
7.1.2. redux是什么
- redux是一个专门用于做状态管理的JS库(不是react插件库)。
2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
3. 作用: 集中式管理react应用中多个组件共享的状态。
 
7.1.3. 什么情况下需要使用redux
- 某个组件的状态,需要让其他组件可以随时拿到(共享)。
2. 一个组件需要改变另一个组件的状态(通信)。
3. 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。
 
7.1.4. redux工作流程

7.2. redux的三个核心概念
7.2.1. action
- 动作的对象
2. 包含2个属性 
- type:标识属性, 值为字符串, 唯一, 必要属性
 
- data:数据属性, 值类型任意, 可选属性
 
- 例子:{ type: ‘ADD_STUDENT’,data:{name: ‘tom’,age:18} }
 
**7.2.2. reducer **
- 用于初始化状态、加工状态。
2. 加工时,根据旧的state和action, 产生新的state的纯函数。
 
7.2.3. store
- 将state、action、reducer联系在一起的对象
2. 如何得到此对象?
1) import {createStore} from ‘redux’
2) import reducer from ‘./reducers’
3) const store = createStore(reducer)
3. 此对象的功能?
1) getState(): 得到state
2) dispatch(action): 分发action, 触发reducer调用, 产生新的state
3) subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
 
7.3. redux的核心API
7.3.1. createstore()
作用:创建包含指定reducer的store对象
7.3.2. store对象
- 作用: redux库最核心的管理对象
2. 它内部维护着:
1) state
2) reducer
3. 核心方法:
1) getState()
2) dispatch(action)
3) subscribe(listener)
4. 具体编码:
1) store.getState()
2) store.dispatch({type:’INCREMENT’, number})
3) store.subscribe(render)
 
7.3.3. applyMiddleware()
作用:应用上基于redux的中间件(插件库)
7.3.4. combineReducers()
作用:合并多个reducer函数
7.4. 使用redux编写应用
**	效果**

精简版
(1).去除Count组件自身的状态
(2).src下建立:
        -redux
          -store.js
          -count_reducer.js
(3).store.js:
      1).引入redux中的createStore函数,创建一个store
      2).createStore调用时要传入一个为其服务的reducer
      3).记得暴露store对象
(4).count_reducer.js:
      1).reducer的本质是一个函数,接收:preState,action,返回加工后的状态
      2).reducer有两个作用:初始化状态,加工状态
      3).reducer被第一次调用时,是store自动触发的,
              传递的preState是undefined,
              传递的action是:{type:‘@@REDUX/INIT_a.2.b.4}
(5).在index.js中监测store中状态的改变,一旦发生改变重新渲染
    备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
目录结构

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")); });
 
   | 
 
import React, { Component } from 'react' import Count from './components/Count'
  export default class App extends Component { 	render() { 		return ( 			<div> 				<Count/> 			</div> 		) 	} }
 
  | 
 
 
 
 
  import { createStore } from "redux";
  import countReducer from "./count_reducer";
  export default createStore(countReducer);
 
 
  | 
 
 
 
 
  const initState = 0;  export default function countReducer(preState = initState, action) {         const { type, data } = action;      switch (type) {     case "increment":        return preState + data;     case "decrement":        return preState - data;     default:       return preState;   } }
 
 
  | 
 
import React, { Component } from "react";
  import store from "../../redux/store";
  export default class Count extends Component {      state = { carName: "奔驰c63" };
    
 
 
 
 
 
 
 
       increment = () => {     const { value } = this.selectNumber;     store.dispatch({ type: "increment", data: value * 1 });   };      decrement = () => {     const { value } = this.selectNumber;     store.dispatch({ type: "decrement", data: value * 1 });   };      incrementIfOdd = () => {     const { value } = this.selectNumber;     const count = store.getState();     if (count % 2 !== 0) {       store.dispatch({ type: "increment", data: value * 1 });     }   };      incrementAsync = () => {     const { value } = this.selectNumber;     setTimeout(() => {       store.dispatch({ type: "increment", data: value * 1 });     }, 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>                   <button onClick={this.increment}>+</button>          <button onClick={this.decrement}>-</button>          <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>          <button onClick={this.incrementAsync}>异步加</button>        </div>     );   } }
 
  | 
 
完整版
新增文件:
  1.count_action.js 专门用于创建action对象
  2.constant.js 放置容易写错的type值
文件结构

 
 
  export const INCREMENT = 'increment' export const DECREMENT = 'decrement'
 
  | 
 
 
  import { INCREMENT, DECREMENT } from "./constant";
 
 
  export const createIncrementAction = (data) => ({ type: INCREMENT, data }); export const createDecrementAction = (data) => ({ type: DECREMENT, data });
 
 
  | 
 
import React, { Component } from "react"; import store from "../../redux/store";
  import {   createIncrementAction,   createDecrementAction, } from "../../redux/count_action";
  export default class Count extends Component {
       increment = () => {     const { value } = this.selectNumber;          store.dispatch(createIncrementAction(value * 1));   };      decrement = () => {     const { value } = this.selectNumber;     store.dispatch(createDecrementAction(value * 1));   };      incrementIfOdd = () => {     const { value } = this.selectNumber;     const count = store.getState();     if (count % 2 !== 0) {       store.dispatch(createIncrementAction(value * 1));     }   };      incrementAsync = () => {     const { value } = this.selectNumber;     setTimeout(() => {       store.dispatch(createIncrementAction(value * 1));     }, 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>                   <button onClick={this.increment}>+</button>          <button onClick={this.decrement}>-</button>          <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>          <button onClick={this.incrementAsync}>异步加</button>        </div>     );   } }
 
  | 
 
其他的无变化
异步Action版
(1).明确:延迟的动作不想交给组件自身,想交给action
(2).何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
(3).具体编码:
    1).yarn add redux-thunk,并配置在store中
    2).创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
    3).异步任务有结果后,分发一个同步的action去真正操作数据。
(4).备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
 
 
 
  import { createStore, applyMiddleware } from "redux";
  import countReducer from "./count_reducer";
  import thunk from "redux-thunk";
  export default createStore(countReducer, applyMiddleware(thunk));
 
 
  | 
 
 
  import { INCREMENT, DECREMENT } from "./constant";
 
  export const createIncrementAction = (data) => ({ type: INCREMENT, data }); export const createDecrementAction = (data) => ({ type: DECREMENT, data });
 
  export const createIncrementAsyncAction = (data, time) => {      return (dispatch) => {     setTimeout(() => {       dispatch(createIncrementAction(data));     }, time);   }; };
 
 
  | 
 
7.5. redux异步编程
上面的就是实例!
7.5.1理解:
- redux默认是不能进行异步处理的, 
2. 某些时候应用中需要在redux中执行异步任务(ajax, 定时器)
 
7.5.2. 使用异步中间件
npm install –save redux-thunk
7.6. react-redux

7.6.1. 理解
- 一个react插件库
2. 专门用来简化react应用中使用redux
 
7.6.2. react-Redux将所有组件分成两大类
- UI组件
1) 只负责 UI 的呈现,不带有任何业务逻辑
2) 通过props接收数据(一般数据和函数)
3) 不使用任何 Redux 的 API
4) 一般保存在components文件夹下
2. 容器组件
1) 负责管理数据和业务逻辑,不负责UI的呈现
2) 负责使用 Redux 的 API,和Redux进行通信
3) 一般保存在containers文件夹下
4)这个组件通过connect()()方法进行生成
 
7.6.3. 相关API
- Provider:让所有组件都可以得到state数据
2. connect:用于包装UI 组件生成容器组件
3. mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
4. mapDispatchToProps:将分发action的函数转换为UI组件的标签属性 
(1).明确两个概念:
      1).UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
      2).容器组件:负责和redux通信,将结果交给UI组件。
(2).如何创建一个容器组件————靠react-redux 的 connect函数
        connect(mapStateToProps,mapDispatchToProps)(UI组件)
          -mapStateToProps:映射状态,返回值是一个对象
          -mapDispatchToProps:映射操作状态的方法,返回值是一个对象
(3).备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
(4).备注2:mapDispatchToProps,也可以是一个对象
import React from 'react' import ReactDOM from 'react-dom' import App from './App' import store from './redux/store'
  ReactDOM.render(<App/>,document.getElementById('root'))
 
 
 
 
 
 
   | 
 
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            这样可以方便 容器组件 得到store中的state和dispatch         */}         <Count store={store} />       </div>     );   } }
 
  | 
 
 import CountUI from "../../components/Count";
  import {   createIncrementAction,   createDecrementAction,   createIncrementAsyncAction, } from "../../redux/count_action";
 
  import { connect } from "react-redux";
 
 
 
 
 
 
 
 
 
 
  function mapStateToProps(state) {   return { count: state }; }
 
 
 
 
 
 
  function mapDispatchToProps(dispatch) {   return {     jia: (number) => dispatch(createIncrementAction(number)),     jian: (number) => dispatch(createDecrementAction(number)),     jiaAsync: (number, time) =>       dispatch(createIncrementAsyncAction(number, time)),   }; }
 
 
  export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
 
 
  | 
 
import React, { Component } from 'react'
  export default class Count extends Component {
  	state = {carName:'奔驰c63'}
  	 	increment = ()=>{ 		const {value} = this.selectNumber 		this.props.jia(value*1) 	} 	 	decrement = ()=>{ 		const {value} = this.selectNumber 		this.props.jian(value*1) 	} 	 	incrementIfOdd = ()=>{ 		const {value} = this.selectNumber 		if(this.props.count % 2 !== 0){ 			this.props.jia(value*1) 		} 	} 	 	incrementAsync = ()=>{ 		const {value} = this.selectNumber 		this.props.jiaAsync(value*1,500) 	}
  	render() { 		 		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>  				<button onClick={this.increment}>+</button>  				<button onClick={this.decrement}>-</button>  				<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>  				<button onClick={this.incrementAsync}>异步加</button>  			</div> 		) 	} }
 
  | 
 
优化版
(1).容器组件和UI组件整合一个文件
(2).无需自己给容器组件传递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读取和操作状态
import React, { Component } from 'react'
  import { 	createIncrementAction, 	createDecrementAction, 	createIncrementAsyncAction } from '../../redux/count_action'
  import {connect} from 'react-redux'
 
  class Count extends Component {
  	state = {carName:'奔驰c63'}
  	 	increment = ()=>{ 		const {value} = this.selectNumber 		this.props.jia(value*1) 	} 	 	decrement = ()=>{ 		const {value} = this.selectNumber 		this.props.jian(value*1) 	} 	 	incrementIfOdd = ()=>{ 		const {value} = this.selectNumber 		if(this.props.count % 2 !== 0){ 			this.props.jia(value*1) 		} 	} 	 	incrementAsync = ()=>{ 		const {value} = this.selectNumber 		this.props.jiaAsync(value*1,500) 	}
  	render() { 		 		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>  				<button onClick={this.increment}>+</button>  				<button onClick={this.decrement}>-</button>  				<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>  				<button onClick={this.incrementAsync}>异步加</button>  			</div> 		) 	} }
 
  export default connect( 	state => ({count:state}),
  	 	
 
 
 
 
  	 	{ 		jia:createIncrementAction, 		jian:createDecrementAction, 		jiaAsync:createIncrementAsyncAction, 	} )(Count)
 
 
  | 
 
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") );
 
   | 
 
数据共享版
目录结构

相关代码
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") );
 
   | 
 
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>     );   } }
 
  | 
 
 
  import {INCREMENT,DECREMENT} from '../constant'
 
  export const createIncrementAction = data => ({type:INCREMENT,data}) export const createDecrementAction = data => ({type:DECREMENT,data})
 
  export const createIncrementAsyncAction = (data,time) => { 	return (dispatch)=>{ 		setTimeout(()=>{ 			dispatch(createIncrementAction(data)) 		},time) 	} }
 
  | 
 
import {ADD_PERSON} from '../constant'
 
  export const createAddPersonAction = personObj => ({type:ADD_PERSON,data:personObj})
  | 
 
 
 
  import { INCREMENT, DECREMENT } from "../constant";
  const initState = 0;  export default function countReducer(preState = initState, action) {      const { type, data } = action;      switch (type) {     case INCREMENT:        return preState + data;     case DECREMENT:        return preState - data;     default:       return preState;   } }
 
 
  | 
 
import { ADD_PERSON } from "../constant";
 
  const initState = [{ id: "001", name: "tom", age: 18 }];
  export default function personReducer(preState = initState, action) {   const { type, data } = action;   switch (type) {     case ADD_PERSON:                      return [data, ...preState];     default:       return preState;   } }
 
  | 
 
 
 
 
  import { createStore, applyMiddleware, combineReducers } from "redux";
  import countReducer from "./reducers/count";
  import personReducer from "./reducers/person";
  import thunk from "redux-thunk";
  import { composeWithDevTools } from "redux-devtools-extension";
 
  const allReducer = combineReducers({   he: countReducer,   rens: personReducer, });
 
 
 
  export default createStore(   allReducer,   composeWithDevTools(applyMiddleware(thunk)) );
 
 
  | 
 
import React, { Component } from "react";
  import {   createIncrementAction,   createDecrementAction,   createIncrementAsyncAction, } from "../../redux/actions/count";
  import { connect } from "react-redux";
 
  class Count extends Component {      increment = () => {     const { value } = this.selectNumber;     this.props.jia(value * 1);   };      decrement = () => {     const { value } = this.selectNumber;     this.props.jian(value * 1);   };      incrementIfOdd = () => {     const { value } = this.selectNumber;     if (this.props.count % 2 !== 0) {       this.props.jia(value * 1);     }   };      incrementAsync = () => {     const { value } = this.selectNumber;     this.props.jiaAsync(value * 1, 500);   };
    render() {          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>                   <button onClick={this.increment}>+</button>          <button onClick={this.decrement}>-</button>          <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>          <button onClick={this.incrementAsync}>异步加</button>        </div>     );   } }
 
  export default connect(   (state) => ({     count: state.he,     renshu: state.rens.length,   }),   {     jia: createIncrementAction,     jian: createDecrementAction,     jiaAsync: createIncrementAsyncAction,   } )(Count);
 
  | 
 
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.value;     const age = this.ageNode.value * 1;     const 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);
 
  | 
 
运行效果

最终版
(1).所有变量名字要规范,尽量触发对象的简写形式。
(2).reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer
 
 
  import {combineReducers} from 'redux'
  import count from './count'
  import persons from './person'
 
  export default  combineReducers({ 	count, 	persons })
 
 
  | 
 
7.7. 使用上redux调试工具
(1).yarn add redux-devtools-extension
(2).store中进行配置
    import {composeWithDevTools} from ‘redux-devtools-extension’
    const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
7.7.1. 安装chrome浏览器插件

7.7.2. 下载工具依赖包
npm install –save-dev redux-devtools-extension
7.7.3. 配置
 
 
 
  import { createStore, applyMiddleware, combineReducers } from "redux";
  import countReducer from "./reducers/count";
  import personReducer from "./reducers/person";
  import thunk from "redux-thunk";
  import { composeWithDevTools } from "redux-devtools-extension";
 
  const allReducer = combineReducers({   he: countReducer,   rens: personReducer, });
 
 
 
  export default createStore(   allReducer,   composeWithDevTools(applyMiddleware(thunk)) );
 
 
  | 
 
7.8. 纯函数和高阶函数
7.8.1. 纯函数
- 一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)
2. 必须遵守以下一些约束  
1) 不得改写参数数据
2) 不会产生任何副作用,例如网络请求,输入和输出设备
3) 不能调用Date.now()或者Math.random()等不纯的方法  
3. redux的reducer函数必须是一个纯函数
 
7.8.2. 高阶函数
- 理解: 一类特别的函数
1) 情况1: 参数是函数
2) 情况2: 返回是函数
2. 常见的高阶函数: 
1) 定时器设置函数
2) 数组的forEach()/map()/filter()/reduce()/find()/bind()
3) promise
4) react-redux中的connect函数
3. 作用: 能实现更加动态, 更加可扩展的功能 
扩展
1. setState
setState更新状态的2种写法
(1). setState(stateChange, [callback])——对象式的setState
  1.stateChange为状态改变对象(该对象可以体现出状态的更改)
  2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
(2). setState(updater, [callback])——函数式的setState
  1.updater为返回stateChange对象的函数。
  2.updater可以接收到state和props。
  3.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:
  1.对象式的setState是函数式的setState的简写方式(语法糖)
  2.使用原则:
          (1).如果新状态不依赖于原状态 ===> 使用对象方式
          (2).如果新状态依赖于原状态 ===> 使用函数方式
          (3).如果需要在setState()执行后获取最新的状态数据,(因为react在更新state的时候是异步的), 要在第二个callback函数中读取
import React, { Component } from "react";
  export default class Demo extends Component {   state = { count: 0 };
    add = () => {               const { count } = this.state;          this.setState({ count: count + 1 }, () => {       console.log(this.state.count);      });     console.log("12行的输出", this.state.count); 
           this.setState((state) => ({ count: state.count + 1 }));    };
    render() {     return (       <div>         <h1>当前求和为:{this.state.count}</h1>         <button onClick={this.add}>点我+1</button>       </div>     );   } }
 
  | 
 
2. lazyLoad
路由组件的lazyLoad
一般用作路由组件的懒加载,平时的路由是只要启动的话就会全部加载,懒加载处理后就会用到哪个加载哪个
import React, { Component, lazy, Suspense } from "react"; import { NavLink, Route } from "react-router-dom";
 
 
 
 
 
  import Loading from "./Loading";
  const Home = lazy(() => import("./Home")); const About = lazy(() => import("./About"));
  export default class Demo 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">               {/* 在React中靠路由链接实现切换组件--编写路由链接 */}               <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">                 {/*                    这里必须要指定这个标签,意思是下面路由组件正在加载的时候显示的组件                   如果没有这个标签的话会报错                 */}                 <Suspense fallback={<Loading />}>                   {/* 注册路由 */}                   <Route path="/about" component={About} />                   <Route path="/home" component={Home} />                 </Suspense>               </div>             </div>           </div>         </div>       </div>     );   } }
 
  | 
 
3. Hooks
1. React Hook/Hooks是什么?
(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性
2. 三个常用的Hook
(1). State Hook: React.useState()
(2). Effect Hook: React.useEffect() 使用生命周期
(3). Ref Hook: React.useRef()
3. 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): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
4. Effect Hook
(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用操作:
        发ajax请求数据获取
        设置订阅 / 启动定时器
        手动更改真实DOM
(3). 语法和说明: 
        useEffect(() => { 
          // 在此可以执行任何带副作用操作
          return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
          }
        }, [stateValue]) //这里可以指定是检测哪个state的update操作。 如果指定的是[], 回调函数只会在第一次render()后执行
    
(4). 可以把 useEffect Hook 看做如下三个函数的组合
        componentDidMount()
        componentDidUpdate()
        componentWillUnmount()
5. Ref Hook
这个很像creatRef,专人专用
(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样
import React from "react"; import ReactDOM from "react-dom";
 
  function 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) => 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
jsx语法是不能出现多个根标签,Fragment可以做那个根标签,并且react在渲染真实dom的时候会直接忽略这个标签
这个很像vue里面的template标签,但是相比较template不能做根标签
使用
<></>
import React, { Component, Fragment } from "react";
  export default class Demo extends Component {   render() {     return (       // <Fragment key={1}>       // 	<input type="text"/>       // 	<input type="text"/>       // </Fragment>       //这个和上面的区别只有上面可以传递key属性,其他的没什么区别       <>         <input type="text" />         <input type="text" />       </>     );   } }
 
  | 
 
这两个是差不多的,但是Fragment标签可以传输key属性
作用
可以不用必须有一个真实的DOM根标签了
5. Context
理解
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
也就是A和C、D间的通信
使用
1) 创建Context容器对象: const XxxContext = React.createContext()  
  2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:   <xxxContext.Provider value={数据}>   子组件   </xxxContext.Provider>
  3) 后代组件读取数据:
       static contextType = xxxContext     this.context 
       <xxxContext.Consumer>   {     value => (        要显示的内容     ) } </xxxContext.Consumer>
   | 
 
具体代码
import React, { Component } from "react"; import "./index.css";
 
  const MyContext = React.createContext(); const { Provider, Consumer } = MyContext; export default class A extends Component {   state = { username: "tom", age: 18 };
    render() {     const { username, age } = this.state;     return (       <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>     );   } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  function C() {   return (     <div className="grand">       <h3>我是C组件</h3>       <h4>         我从A组件接收到的用户名:         <Consumer>{(value) => `${value.username},年龄是${value.age}`}</Consumer>       </h4>     </div>   ); }
 
  | 
 
注意
在应用开发中一般不用context, 一般都用它的封装react插件(react-redux)
6. 组件优化
Component的2个问题
- 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
 
- 只要当前组件重新render(), 就会自动重新render子组件(父组件render,子组件也得render),纵使子组件没有用到父组件的任何数据 ==> 效率低
 
效率高的做法
只有当组件的state或props数据发生改变时才重新render()
原因
Component中的shouldComponentUpdate()总是返回true
解决
办法1: 
	重写shouldComponentUpdate()方法
	比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
办法2:  
	使用PureComponent
	PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
	注意: 
		只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false  
		不要直接修改state数据, 而是要产生新数据(创建新对象)
项目中一般使用PureComponent来优化
import React, { Component } from 'react' import './index.css'
  export default class Parent extends Component {
    state = {carName:"奔驰c36"}
    changeCar = ()=>{     this.setState({carName:'迈巴赫'})   }
    shouldComponentUpdate(nextProps,nextState){               return !this.state.carName === nextState.carName   } 
    render() {     console.log('Parent---render');     const {carName} = this.state     return (       <div className="parent">         <h3>我是Parent组件</h3>         {this.state.stus}          <span>我的车名字是:{carName}</span><br/>         <button onClick={this.changeCar}>点我换车</button>         <Child carName="奥拓"/>       </div>     )   } }
  class Child extends Component {
    shouldComponentUpdate(nextProps,nextState){     console.log(this.props,this.state);      console.log(nextProps,nextState);      return !this.props.carName === nextProps.carName   } 
    render() {     console.log('Child---render');     return (       <div className="child">         <h3>我是Child组件</h3>         <span>我接到的车是:{this.props.carName}</span>       </div>     )   } }
 
  | 
 
import React, { PureComponent } from "react"; import "./index.css";
  export default class Parent extends PureComponent {   state = { carName: "奔驰c36"};   changeCar = () => {     this.setState({carName:'迈巴赫'})   };   render() {     console.log("Parent---render");     const { carName } = this.state;     return (       <div className="parent">         <h3>我是Parent组件</h3>         <span>我的车名字是:{carName}</span>         <br />         <button onClick={this.changeCar}>点我换车</button>         <Child carName="奥拓" />       </div>     );   } }
  class Child extends PureComponent {   render() {     console.log("Child---render");     return (       <div className="child">         <h3>我是Child组件</h3>         <span>我接到的车是:{this.props.carName}</span>       </div>     );   } }
 
  | 
 
7. render props
如何向组件内部动态传入带内容的结构(标签)?
Vue中: 
	使用slot技术, 也就是通过组件标签体传入结构  
React中:
	使用children props: 通过组件标签体传入结构
	使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
children props
<A>   <B>xxxx</B> </A> {this.props.children} 问题: 如果B组件需要A组件内的数据, ==> 做不到
   | 
 
具体代码
import React, { Component } from "react"; import "./index.css";
  export default class Parent extends Component {   render() {     return (       <div className="parent">         <h3>我是Parent组件</h3>         <A>           <B />         </A>       </div>     );   } }
  class A extends Component {   state = { name: "tom" };   render() {     console.log(this.props);     const { name } = this.state;     return (       <div className="a">         <h3>我是A组件</h3>         {/* 渲染B,但是这里没办法传递props */}         {this.props.children()}       </div>     );   } }
  class B extends Component {   render() {     console.log("B--render");     return (       <div className="b">         <h3>我是B组件</h3>       </div>     );   } }
 
  | 
 
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";
  export default class Parent extends Component {   render() {     return (       <div className="parent">         <h3>我是Parent组件</h3>         {/* 渲染A,并且把B作为A的子组件传入进去,并且可以把A的name属性传入进去 */}         <A render={(name) => <B name={name} />} />       </div>     );   } }
  class A extends Component {   state = { name: "tom" };   render() {     console.log(this.props);     const { name } = this.state;     return (       <div className="a">         <h3>我是A组件</h3>         {/* 在这里进行渲染B并且把A的name给传入进去 */}         {this.props.render(name)}       </div>     );   } }
  class B extends Component {   render() {     console.log("B--render");     return (       <div className="b">         {/* 接收到A的name属性 */}         <h3>我是B组件,{this.props.name}</h3>       </div>     );   } }
 
  | 
 
8. 错误边界
开发环境还是会报错,生产环境不会报错
理解:
错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面
错误边界需要在容易发生错误的子组件的父组件做一些配置,如果子组件出现了错误,父组件仍然可以展示,并且会把子组件渲染成一个专门用于展示错误的组件,只把错误限制到子组件中,父组件仍然可以展示
特点:
只能捕获后代组件生命周期函数中产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
使用方式:
getDerivedStateFromError配合componentDidCatch
 static getDerivedStateFromError(error) {   console.log(error);         return {     hasError: true,   }; }
  componentDidCatch(error, info) {      console.log(error, info); }
 
  | 
 
具体实例:
import React, { Component } from "react"; import Child from "./Child";
  export default class Parent extends Component {   state = {     hasError: "",    };
       static getDerivedStateFromError(error) {     console.log("@@@", error);      return { hasError: error };    }
       componentDidCatch() {     console.log("此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决");   }
    render() {     return (       <div>         <h2>我是Parent组件</h2>         {/* 下面判断hasError是否为null或者是空字符串进行显示 */}         {this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child />}       </div>     );   } }
 
  | 
 
import React, { Component } from "react";
  export default class Child extends Component {   state = {     users: [       { id: "001", name: "tom", age: 18 },       { id: "002", name: "jack", age: 19 },       { id: "003", name: "peiqi", age: 20 },     ],        };
    render() {     return (       <div>         <h2>我是Child组件</h2>         {this.state.users.map((userObj) => {           return (             <h4 key={userObj.id}>               {userObj.name}----{userObj.age}             </h4>           );         })}       </div>     );   } }
 
  | 
 
9. 组件通信方式总结
组件间的关系:
- 父子组件
 
- 兄弟组件(非嵌套组件)
 
- 祖孙组件(跨级组件)
 
几种通信方式:
1.props:
  (1).children props
  (2).render props
2.消息订阅-发布:
 	 pubs-sub
3.集中式管理:
 	 redux
4.conText:
 	 生产者-消费者模式
比较好的搭配方式:
父子组件:props
兄弟组件:消息订阅-发布、集中式管理
祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)
打包部署
npm run build  打包
npm i serve -g  安装serve
serve -s build 利用serve启动应用
React Router 6 快速上手

1.概述
- React Router 以三个不同的包发布到 npm 上,它们分别为:
- react-router: 路由的核心库,提供了很多的:组件、钩子。
 
- **react-router-dom(我们学这个):包含react-router所有内容,并添加一些专门用于 DOM 的组件,例如 等 **。
 
- react-router-native: 包括react-router所有内容,并添加一些专门用于ReactNative的API,例如:等。
 
 
- 与React Router 5.x 版本相比,改变了什么?
- 内置组件的变化:移除 ,新增 等。
 
- 语法的变化:component={About} 变为 element={}等。
 
- 新增多个hook:useParams、useNavigate、useMatch等。
 
- 新增多个hook说明!官方明确推荐函数式组件了!!!……
 
 
安装react-router
npm i react-router-dom
2.Component
1. 
- 说明:用于包裹整个应用。
 
- 示例代码:
 
import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter } from "react-router-dom";
  ReactDOM.render(   <BrowserRouter>     {/* 整体结构(通常为App组件) */}   </BrowserRouter>,root );
   | 
 
2. 
- 说明:作用与一样,但修改的是地址栏的hash值。
 
- 备注:6.x版本中、 的用法与 5.x 相同。
 
3.  与 
- v6版本中移出了先前的,引入了新的替代者:。
 
-  和 要配合使用,且必须要用包裹。
 
-  相当于一个 if 语句,如果其路径与当前 URL 匹配,则呈现其对应的组件。
 
-  属性用于指定:匹配时是否区分大小写(默认为 false)。
 
- 当URL发生变化时,都会查看其所有子 元素以找到最佳匹配并呈现组件 。
 
-  也可以嵌套使用,且可配合useRoutes()配置 “路由表” ,但需要通过  组件来渲染其子路由。
 
- 示例代码:
 
<Routes>      <Route path="/login" element={<Login />}></Route>
       <Route path="home" element={<Home />}>     /*test1 和 test2 是二级路由,对应的路径是/home/test1 或 /home/test2*/     <Route path="test1" element={<Test/>}></Route>     <Route path="test2" element={<Test2/>}></Route>   </Route>
       <Route path="users">     <Route path="xxx" element={<Demo />} />   </Route> </Routes>
   | 
 
4. 
- 作用: 修改URL,且不发送网络请求(路由链接)。
 
- 注意: 外侧需要用或包裹。
 
- 示例代码:
 
import { Link } from "react-router-dom";
  function Test() {   return (     <div>       <Link to="/路径">按钮</Link>     </div>   ); }
  | 
 
5. 
- 作用: 与组件类似,且可实现导航的“高亮”效果。
 
- 示例代码:
 
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() {   function computedClassName({ isActive }) {               return isActive ? "list-group-item atguigu" : "list-group-item";   }   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">             {/* 路由链接 */}             {/* className的值可以为一个函数, 这个函数写在了上面*/}             <NavLink className={computedClassName} to="/about">               About             </NavLink>             <NavLink className={computedClassName} 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>   ); }
 
   | 
 
6. 
- 作用:只要组件被渲染,就会修改路径,切换视图。
 
- replace属性用于控制跳转模式(push 或 replace,默认是push)。
 
- 示例代码:
 
import React,{useState} from 'react' import {Navigate} from 'react-router-dom'
  export default function Home() {   const [sum,setSum] = useState(1)   return (     <div>       <h3>我是Home的内容</h3>       {/* 根据sum的值决定是否切换视图,下面显示的是如果sum为1就显示h4标签,否则就跳转路由到about页面,并且为replace模式(与push相照应) */}       {sum === 1 ? <h4>sum的值为{sum}</h4> : <Navigate to="/about" replace={true}/>}       <button onClick={()=>setSum(2)}>点我将sum变为2</button>     </div>   ) }
  | 
 
还可以用来表示默认跳转到哪个页面
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和Switch的区别是Switch是为了提升效率,可写可不写。               但是Routes必须得写,也能实现Switch的功能。 */}               <Routes>                 <Route path="/about" element={<About />} />                 {/* 默认不区分大小写 */}                 <Route path="/HOME" element={<Home />} />                 {/* 这里表示如果路径是/,也就会默认重定向到about路径下,进行渲染About组件 */}                 <Route path="/" element={<Navigate to="/about" />} />               </Routes>             </div>           </div>         </div>       </div>     </div>   ); }
 
   | 
 
7. 
- 当产生嵌套时,渲染其对应的后续子路由。
 
- 示例代码:
 

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";
  export default [   {     path: "/about",     element: <About />,   },   {     path: "/home",     element: <Home />,     children: [       {                  path: "news",         element: <News />,       },       {         path: "message",         element: <Message />,       },     ],   },   {     path: "/",     element: <Navigate to="/about" />,   }, ];
 
   | 
 
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" end to="/home">Home</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>   ); }
 
   | 
 
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="news">               News             </NavLink>           </li>           <li>             <NavLink className="list-group-item" to="message">               Message             </NavLink>           </li>         </ul>         {/* 指定路由组件呈现的位置 */}         <Outlet />       </div>     </div>   ); }
 
   | 
 
3.Hooks
1. useRoutes()
- 作用:根据路由表,动态创建和。
 
- 示例代码:
 
 import About from '../pages/About' import Home from '../pages/Home' import {Navigate} from 'react-router-dom'
  export default [   {     path:'/about',     element:<About/>   },   {     path:'/home',     element:<Home/>   },   {     path:'/',     element:<Navigate to="/about"/>   } ]
 
  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>       ......       {/* 注册路由 */}       {element}       ......     </div>   ) }
 
  | 
 
真实项目使用:

import About from '../pages/About' import Home from '../pages/Home' import {Navigate} from 'react-router-dom'
  export default [ 	{ 		path:'/about', 		element:<About/> 	}, 	{ 		path:'/home', 		element:<Home/> 	}, 	{ 		path:'/', 		element:<Navigate to="/about"/> 	} ]
   | 
 
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> 	) }
 
   | 
 
2. useNavigate()
- 作用:返回一个函数用来实现编程式导航。
 
- 示例代码:
 
import React, { useState } from "react"; import { Link, Outlet, useNavigate } from "react-router-dom";
  export default function Message() {   const navigate = useNavigate();   const [messages] = useState([     { id: "001", title: "消息1", content: "锄禾日当午" },     { id: "002", title: "消息2", content: "汗滴禾下土" },     { id: "003", title: "消息3", content: "谁知盘中餐" },     { id: "004", title: "消息4", content: "粒粒皆辛苦" },   ]);
    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>   ); }
 
  | 
 
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>         <button onClick={back}>←后退</button>         <button onClick={forward}>前进→</button>       </div>     </div>   ); }
 
   | 
 
3. useParams()
- 作用:回当前匹配路由的params参数,类似于5.x中的match.params。
 
- 示例代码:
 

import About from "../pages/About"; import Home from "../pages/Home"; import Message from "../pages/Message"; import News from "../pages/News"; import Detail from "../pages/Detail"; import { Navigate } from "react-router-dom";
  export default [   {     path: "/about",     element: <About />,   },   {     path: "/home",     element: <Home />,     children: [       {         path: "news",         element: <News />,       },       {         path: "message",         element: <Message />,         children: [           {                          path: "detail/:id/:title/:content",             element: <Detail />,           },         ],       },     ],   },   {     path: "/",     element: <Navigate to="/about" />,   }, ];
 
   | 
 
import React, { useState } from "react"; import { Link, Outlet } from "react-router-dom";
  export default function Message() {   const [messages] = useState([     { id: "001", title: "消息1", content: "锄禾日当午" },     { id: "002", title: "消息2", content: "汗滴禾下土" },     { id: "003", title: "消息3", content: "谁知盘中餐" },     { id: "004", title: "消息4", content: "粒粒皆辛苦" },   ]);   return (     <div>       <ul>         {messages.map((m) => {           return (             // 路由链接             <li key={m.id}>               {/* 在这里进行传输参数 */}               <Link to={`detail/${m.id}/${m.title}/${m.content}`}>                 {m.title}               </Link>             </li>           );         })}       </ul>       <hr />       {/* 指定路由组件的展示位置 */}       <Outlet />     </div>   ); }
 
  | 
 
import React from "react"; import { useParams, useMatch } from "react-router-dom";
  export default function Detail() {      const { id, title, content } = useParams();   return (     <ul>       <li>消息编号:{id}</li>       <li>消息标题:{title}</li>       <li>消息内容:{content}</li>     </ul>   ); }
 
   | 
 
4. useSearchParams()
- 作用:用于读取和修改当前位置的 URL 中的查询字符串。
 
- 返回一个包含两个值的数组,内容分别为:当前的seaech参数、更新search的函数。
 
- 示例代码:
 

import About from "../pages/About"; import Home from "../pages/Home"; import Message from "../pages/Message"; import News from "../pages/News"; import Detail from "../pages/Detail"; import { Navigate } from "react-router-dom";
  export default [   {     path: "/about",     element: <About />,   },   {     path: "/home",     element: <Home />,     children: [       {         path: "news",         element: <News />,       },       {         path: "message",         element: <Message />,         children: [           {                          path: "detail",             element: <Detail />,           },         ],       },     ],   },   {     path: "/",     element: <Navigate to="/about" />,   }, ];
 
   | 
 
import React, { useState } from "react"; import { Link, Outlet } from "react-router-dom";
  export default function Message() {   const [messages] = useState([     { id: "001", title: "消息1", content: "锄禾日当午" },     { id: "002", title: "消息2", content: "汗滴禾下土" },     { id: "003", title: "消息3", content: "谁知盘中餐" },     { id: "004", title: "消息4", content: "粒粒皆辛苦" },   ]);   return (     <div>       <ul>         {messages.map((m) => {           return (             // 路由链接             <li key={m.id}>               {/* 用search方式传输参数 */}               <Link                 to={`detail?id=${m.id}&title=${m.title}&content=${m.content}`}               >                 {m.title}               </Link>             </li>           );         })}       </ul>       <hr />       {/* 指定路由组件的展示位置 */}       <Outlet />     </div>   ); }
 
  | 
 
import React from 'react' import {useSearchParams} 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') 	return ( 		<ul> 			<li>消息编号:{id}</li> 			<li>消息标题:{title}</li> 			<li>消息内容:{content}</li> 		</ul> 	) }
 
   | 
 
5. useLocation()
- 作用:获取当前 location 信息,对标5.x中的路由组件的location属性。并且这里面可以获取state参数
 
- 示例代码:
 

import About from "../pages/About"; import Home from "../pages/Home"; import Message from "../pages/Message"; import News from "../pages/News"; import Detail from "../pages/Detail"; import { Navigate } from "react-router-dom";
  export default [   {     path: "/about",     element: <About />,   },   {     path: "/home",     element: <Home />,     children: [       {         path: "news",         element: <News />,       },       {         path: "message",         element: <Message />,         children: [           {                          path: "detail",             element: <Detail />,           },         ],       },     ],   },   {     path: "/",     element: <Navigate to="/about" />,   }, ];
 
   | 
 
import React, { useState } from "react"; import { Link, Outlet } from "react-router-dom";
  export default function Message() {   const [messages] = useState([     { id: "001", title: "消息1", content: "锄禾日当午" },     { id: "002", title: "消息2", content: "汗滴禾下土" },     { id: "003", title: "消息3", content: "谁知盘中餐" },     { id: "004", title: "消息4", 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>             </li>           );         })}       </ul>       <hr />       {/* 指定路由组件的展示位置 */}       <Outlet />     </div>   ); }
 
  | 
 
import React from "react"; import { useLocation } from "react-router-dom";
  export default function Detail() {         const {     state: { id, title, content },   } = useLocation();   return (     <ul>       <li>消息编号:{id}</li>       <li>消息标题:{title}</li>       <li>消息内容:{content}</li>     </ul>   ); }
 
   | 
 
6. useMatch()
- 作用:返回当前匹配信息,对标5.x中的路由组件的match属性。
 
- 示例代码:
 
<Route path="/login/:page/:pageSize" element={<Login />}/> <NavLink to="/login/1/10">登录</NavLink>
  export default function Login() {   const match = useMatch('/login/:x/:y')   console.log(match) //输出match对象   //match对象内容如下:   /*     {       params: {x: '1', y: '10'}       pathname: "/LoGin/1/10"         pathnameBase: "/LoGin/1/10"       pattern: {         path: '/login/:x/:y',          caseSensitive: false,          end: false       }     }   */   return (     <div>       <h1>Login</h1>     </div>   ) }
  | 
 
7. useInRouterContext()
作用:如果组件在  的上下文中呈现,则 useInRouterContext 钩子返回 true,否则返回 false。
App及其子组件都会处于上下文环境中
import React from "react"; import { useInRouterContext } from "react-router-dom";
  export default function Demo() {      console.log(useInRouterContext());   return <div>Demo</div>; }
 
   | 
 
8. useNavigationType()
- 作用:返回当前的导航类型(用户是如何来到当前页面的)。
 
- 返回值:POP、PUSH、REPLACE。
 
- 备注:POP是指在浏览器中直接打开了这个路由组件(刷新页面)。
 
9. useOutlet()
- 作用:用来呈现当前组件中渲染的嵌套路由。
 
- 示例代码:
 
const result = useOutlet() console.log(result)
 
 
   | 
 
10.useResolvedPath()
用来解析路径的。
作用:给定一个 URL值,解析其中的:path、search、hash值。

