目录
5.3.3函数式组件中使用props(只能使用props,state和refs不能使用)
5.4.4事件处理
6.1. 使用create-react-app创建react应用
6.2.1子组件给父组件传递数据
7.13withRouter的使用(把一般组件变成路由组件才有的api)

React是用于构建用户界面的JavaScript库,react只关注界面(或者试图)
是一个将数据渲染为HTML视图的JavaScript库,你给我数据,我帮你渲染视图
需求:在页面上面展示学生的信息?
步骤: 1,发送请求获取数据(ajax)
2,处理数据(遍历,过滤,整理格式)
3,操作DOM呈现视图 (使用js,jquery将数据放入指定的节点里面) [react来做]
Facebook开发,且开源




产生的问题:开始是两个人,页面上面产生了两个真实DOM,现在增加了一个人,三个人,页面上面产生了三个DOM,相当于每次操作都是产生新的DOM,没有进行复用
React的做法:

在数据进行变动的时候,虚拟DOM中会进行比较,假设遇到相同的就不会新的真实DOM,直接复用上次真实的DOM


- react.development.js:React核心库。
- react-dom.js:提供操作DOM的react扩展库。
- bbel.min.js:解析JSX语法代码转为JS代码的库。

- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>hello-react</title>
- </head>
- <body>
- <!-- 准备一个容器 -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
- <script type="text/babel">
- //1.创建虚拟DOM
- const VDOM = <h1>Hello,React</h1> /* 此处一定不要写引号,因为不是字符串 */
- //2.渲染虚拟DOM到页面
- ReactDOM.render(VDOM,document.getElementById('test'))
- </script>
- </body>
- </html>


- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>1_使用jsx创建虚拟DOM</title>
- </head>
- <body>
- <!-- 准备一个容器 -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
- <script type="text/babel">
- //1.创建虚拟DOM
- const VDOM = <h1 id="title">Hello,React</h1> /* 此处一定不要写引号,因为不是字符串 */
- //2.渲染虚拟DOM到页面
- ReactDOM.render(VDOM,document.getElementById('test'))
- </script>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>2_使用js创建虚拟DOM</title>
- </head>
- <body>
- <!-- 准备好一个“容器” -->
- <div id="test"></div>
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
-
- <script type="text/javascript" >
- //1.创建虚拟DOM
- const VDOM = React.createElement('h1',{id:'title'},'Hello,React')
- //2.渲染虚拟DOM到页面
- ReactDOM.render(VDOM,document.getElementById('test'))
- </script>
- </body>
- </html>
需求:将hello,react写在里面
jsx写法

原生js写法

原生js创建虚拟DOM太繁琐了,react打造的jsx简化了创建虚拟DOM的过程,所以REACT使用jsx


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

1.定义虚拟DOM时,不要写引号。
2.标签中混入JS表达式时要用{}。
3.样式的类名指定不要用class,要用className。
4.内联样式,要用style={{key:value}}的形式去写。
5.只有一个根标签
6.标签必须闭合
7.标签首字母
(1).若小写字母开头,则将该标签转为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>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <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>




| 一定注意区分:【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} |
1.理解:向外提供特定功能的js程序, 一般就是一个js文件
2.为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂。
3.作用:复用js, 简化js的编写, 提高js运行效率
1.理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)
2.为什么要用组件: 一个界面的功能更复杂
3.作用:复用编码, 简化项目编码, 提高运行效率
5.1.1. 使用React开发者工具调试

5.1.2. 创建组件的两种方式
方式一:函数式组件(函数名就是组件名)

- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>创建组件</title>
- </head>
- <body>
- <!-- 准备一个容器 -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
- <script type="text/babel">
- //1.创建函数式组件
- function MyComponent(){
- console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
- return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
- }
- //2.渲染组件到页面
- 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>1_类的基本知识</title>
- </head>
- <body>
- <script type="text/javascript" >
- /*
- 总结:
- 1.类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
- 2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
- 3.类中所定义的方法,都放在了类的原型对象上,供实例去使用。
- */
- //创建一个Person类
- class Person {
- //构造器方法
- constructor(name,age){
- //构造器中的this是谁?—— 类的实例对象
- this.name = name
- this.age = age
- }
- //一般方法
- speak(){
- //speak方法放在了哪里?——类的原型对象上,供实例使用
- //通过Person实例调用speak时,speak中的this就是Person实例
- console.log(`我叫${this.name},我年龄是${this.age}`);
- }
- }
-
- //创建一个Student类,继承于Person类
- class Student extends Person {
- constructor(name,age,grade){
- super(name,age)
- this.grade = grade
- }
- //重写从父类继承过来的方法
- speak(){
- console.log(`我叫${this.name},我年龄是${this.age},我读的是${this.grade}年级`);
- this.study()
- }
- study(){
- //study方法放在了哪里?——类的原型对象上,供实例使用
- //通过Student实例调用study时,study中的this就是Student实例
- console.log('我很努力的学习');
- }
- }
- </script>
- </body>
- </html>
总结:
1.类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
3.类中所定义的方法,都放在了类的原型对象上,供实例去使用。
开始创建类式组件:

类式组建创建的必要条件:
(1)必须继承React.Component
(2)必须有render方法
(3)必须要有return 返回值
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>创建组件</title>
- </head>
- <body>
- <!-- 准备一个容器 -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
- <script type="text/babel">
- //1.创建类式组件
- class MyComponent extends React.Component{
- render(){
- return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
- }
- }
- //2.渲染组件到页面
- ReactDOM.render(<MyComponent/>,document.getElementById('test'))
- </script>
- </body>
- </html>
执行了ReactDOM.render(
.......之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。


三大核心属性主要探讨的是类式组件,因为函数式组件没有实例对象,没有自己的this
需求: 定义一个展示天气信息的组件


需求2
react里面的点击事件

将函数调用方法放入组件内


this问题:
构造器中的this一定是当前实例的对象,构造器就是用来创建实例对象的
render()里面的this是因为react创建了组件实例对象,然后通过实例对象调用render()方法
changeWeather()中的this是undefined,说明changeWeather不是通过当前实例对象调用的,因为changeWeather()是点击事件onClick的回调,不是通过实例调用,是直接调用,所以这里的this就是undefined

在类中写的自定义的方法,一般都是作为事件的回调去调用的,这样他们的this就会丢失,this就是undefined
(方法一:)解决changeWeather中的this指向问题,在构造器中进行绑定函数

那么此时this就能找到了

- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>state</title>
- </head>
- <body>
- <!-- 准备好一个“容器” -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
-
- <script type="text/babel">
- //1.创建组件
- class Weather extends React.Component{
-
- //构造器调用几次? ———— 1次
- constructor(props){
- console.log('constructor');
- super(props)
- //初始化状态
- this.state = {isHot:false,wind:'微风'}
- //解决changeWeather中this指向问题
- this.changeWeather = this.changeWeather.bind(this)
- }
-
- //render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
- render(){
- console.log('render');
- //读取状态
- const {isHot,wind} = this.state
- return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
- }
-
- //changeWeather调用几次? ———— 点几次调几次
- changeWeather(){
- //changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
- //由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
- //类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
-
- console.log('changeWeather');
- //获取原来的isHot值
- const isHot = this.state.isHot
- //严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
- this.setState({isHot:!isHot})
-
- }
- }
- //2.渲染组件到页面
- ReactDOM.render(<Weather/>,document.getElementById('test'))
-
- </script>
- </body>
- </html>
总结:
构造器只调用一次,因为只创建了一次实例对象
render()调用多次,实例初始化完成后就是调用一次
changeWeather是点击几次就会调用几次

(方法二)简写方式:寻找this

箭头函数没有自己的this,如果里面写了this就会去找箭头函数外侧的this作为自己的this,以后在类中自定义方法都要写成赋值语句+箭头函数

属性的初识值通常通过组件的属性值进行传递,并合并到组件实例对象的this.props中。



this.props.属性名

需要引入prop-types库
- <!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>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
- <!-- 引入prop-types,用于对组件标签属性进行限制 -->
- <script type="text/javascript" src="../js/prop-types.js"></script>
-
- <script type="text/babel">
- //创建组件
- class Person extends React.Component{
- render(){
- // console.log(this);
- const {name,age,sex} = this.props
- //props是只读的
- //this.props.name = 'jack' //此行代码会报错,因为props是只读的
- return (
- <ul>
- <li>姓名:{name}</li>
- <li>性别:{sex}</li>
- <li>年龄:{age+1}</li>
- </ul>
- )
- }
- }
- //对标签属性进行类型、必要性的限制
- Person.propTypes = {
- name:PropTypes.string.isRequired, //限制name必传,且为字符串
- sex:PropTypes.string,//限制sex为字符串
- age:PropTypes.number,//限制age为数值
- speak:PropTypes.func,//限制speak为函数
- }
- //指定默认标签属性值
- Person.defaultProps = {
- sex:'男',//sex默认值为男
- age:18 //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:'女'}
- // console.log('@',...p);
- // ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3'))
- ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
-
- function speak(){
- console.log('我说话了');
- }
- </script>
- </body>
- </html>
简写方式


props与state的区别
props不能被其所在的组件修改,从父组件传递进来的属性不会在组件内部更改;state只能在所在组件内部更改,或在外部调用setState函数对状态进行间接修改。
需求
需求: 自定义组件, 功能说明如下:
1. 点击按钮, 提示第一个输入框中的值
2. 当第2个输入框失去焦点时, 提示这个输入框中的值
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>创建组件</title>
- </head>
- <body>
- <!-- 准备一个容器 -->
- <div id="test1"></div>
- <div id="test2"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
- <script type="text/babel">
- //1.创建类式组件
- class MyComponent extends React.Component{
- //展示左侧输入框的数据
- showData = ()=>{
- const {input1} = this.refs
- alert(input1.value)
- }
- //展示右侧输入框的数据
- showData2 = ()=>{
- const {input2} = this.refs
- alert(input2.value)
- }
- render(){
- //读取状态
- console.log(this)
- return (
- <div>
- <input ref="input1" type="text" placeholder="点击按钮提示数据"/>
- <button onClick={this.showData}>点我提示左侧的数据</button>
- <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
- </div>
- )
- }
- }
-
- //2.渲染组件到页面
- ReactDOM.render(<MyComponent name="tom" sex="男" age="19"/>,document.getElementById('test1'))
- </script>
- </body>
- </html>

- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>创建组件</title>
- </head>
- <body>
- <!-- 准备一个容器 -->
- <div id="test1"></div>
- <div id="test2"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
- <script type="text/babel">
- //1.创建类式组件
- class MyComponent extends React.Component{
- //展示左侧输入框的数据
- showData = ()=>{
- const {input1} = this
- alert(input1.value)
- }
- //展示右侧输入框的数据
- showData2 = ()=>{
- const {input2} = this
- alert(input2.value)
- }
- render(){
- //读取状态
- console.log(this)
- return (
- <div>
- <input ref={(c)=>{this.input1 = c }} type="text" placeholder="点击按钮提示数据"/>
- <button onClick={this.showData}>点我提示左侧的数据</button>
- <input ref={(c)=>{this.input2 = c }} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
- </div>
- )
- }
- }
- //2.渲染组件到页面
- ReactDOM.render(<MyComponent name="tom" sex="男" age="19"/>,document.getElementById('test1'))
- </script>
- </body>
- </html>

- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>4_createRef</title>
- </head>
- <body>
- <!-- 准备好一个“容器” -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
-
- <script type="text/babel">
- //创建组件
- class Demo extends React.Component{
- /*
- React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
- */
- 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>

(1).通过onXxx属性指定事件处理函数(注意大小写)
a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ——为了的高效
(2).通过event.target得到发生事件的DOM元素对象 ———不要过度使用ref
需求: 定义一个包含表单的组件输入用户名密码后,
点击登录提示输入信息
在虚拟DOM节点上声明一个ref属性,并且将创建好的引用赋值给这个ref属性.react会自动将输入框中输入的值放在实例的ref属性上
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>收集表单数据</title>
- </head>
- <body>
- <!-- 准备一个容器 -->
- <div id="test1"></div>
- <div id="test2"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
- <script type="text/babel">
- //1.创建类式组件
- class LoginComponent extends React.Component{
- handleSubmit =(event) => {
- event.preventDefault() //阻止表单提交
- const {username,password} = this
- alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
- }
- render(){
- return(
- <form action="" 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>
- )
- }
- }
- //2.渲染组件到页面
- ReactDOM.render(<LoginComponent />,document.getElementById('test1'))
- </script>
- </body>
- </html>

用state来获取和设置输入元素值的组件,称之为受控组件。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>2_受控组件</title>
- </head>
- <body>
- <!-- 准备好一个“容器” -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <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>
1、受控组件
受控组件依赖于状态
受控组件的修改会实时映射到状态值上,此时可以对输入的内容进行校验
受控组件只有继承React.Component才会有状态
受控组件必须要在表单上使用onChange事件来绑定对应的事件
上述代码的优化方式一(采用高阶函数,颗粒化):
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>高阶函数_函数柯里化</title>
- </head>
- <body>
- <!-- 准备好一个“容器” -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <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>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <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>
需求:定义组件实现以下功能:
1. 让指定的文本做显示 / 隐藏的渐变动画
2. 从完全可见,到彻底消失,耗时2S
3. 点击“不活了”按钮从界面中卸载组件
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>1_引出生命周期</title>
- </head>
- <body>
- <!-- 准备好一个“容器” -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <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
- //减小0.1
- 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>
componentDidMount(){}组件挂载完毕调用,只会调用一次
componentWillUnmount(){}组件将要卸载,调用
render(){}组件初始化渲染,每次状态更新时调用,调用多次
都是通过组件的实例对象进行调用

- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>2_react生命周期(旧)</title>
- </head>
- <body>
- <!-- 准备好一个“容器” -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/babel.min.js"></script>
-
- <script type="text/babel">
- /*
- 1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
- 1. constructor()
- 2. componentWillMount()
- 3. render()
- 4. componentDidMount() =====> 常用
- 一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
- 2. 更新阶段: 由组件内部this.setSate()或父组件render触发
- 1. shouldComponentUpdate()
- 2. componentWillUpdate()
- 3. render() =====> 必须使用的一个
- 4. componentDidUpdate()
- 3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- 1. componentWillUnmount() =====> 常用
- 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
- */
- //创建组件
- class Count extends React.Component{
-
- //构造器
- constructor(props){
- console.log('Count---constructor');
- super(props)
- //初始化状态
- this.state = {count:0}
- }
-
- //加1按钮的回调
- 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>
- )
- }
- }
-
- //父组件A
- 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>
- )
- }
- }
-
- //子组件B
- class B extends React.Component{
- //组件将要接收新的props的钩子
- 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>
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件render触发
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render() =====> 必须使用的一个
4. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>3_react生命周期(新)</title>
- </head>
- <body>
- <!-- 准备好一个“容器” -->
- <div id="test"></div>
-
- <!-- 引入react核心库 -->
- <script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
- <!-- 引入react-dom,用于支持react操作DOM -->
- <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
- <!-- 引入babel,用于将jsx转为js -->
- <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>
-
- <script type="text/babel">
- //创建组件
- class Count extends React.Component{
- /*
- 1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
- 1. constructor()
- 2. getDerivedStateFromProps
- 3. render()
- 4. componentDidMount() =====> 常用
- 一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
- 2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
- 1. getDerivedStateFromProps
- 2. shouldComponentUpdate()
- 3. render()
- 4. getSnapshotBeforeUpdate
- 5. componentDidUpdate()
- 3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- 1. componentWillUnmount() =====> 常用
- 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
- */
- //构造器
- constructor(props){
- console.log('Count---constructor');
- super(props)
- //初始化状态
- this.state = {count:0}
- }
-
- //加1按钮的回调
- add = ()=>{
- //获取原状态
- const {count} = this.state
- //更新状态
- this.setState({count:count+1})
- }
-
- //卸载组件按钮的回调
- death = ()=>{
- ReactDOM.unmountComponentAtNode(document.getElementById('test'))
- }
-
- //强制更新按钮的回调
- force = ()=>{
- this.forceUpdate()
- }
-
- //若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
- 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>
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
- constructor()
- getDerivedStateFromProps
- render()
- componentDidMount()
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
- getDerivedStateFromProps
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate
- componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
重要的勾子
- render:初始化渲染或更新渲染调用
- componentDidMount:开启监听, 发送ajax请求
- componentWillUnmount:做一些收尾工作, 如: 清理定时器
| 经典面试题: 2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】, a. 旧虚拟DOM中找到了与新虚拟DOM相同的key: b. 旧虚拟DOM中未找到与新虚拟DOM相同的key 2. 如果结构中还包含输入类的DOM: |
- xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
- 包含了所有需要的配置(语法检查、jsx编译、devServer…)
- 下载好了所有相关的依赖
- 可以直接运行一个简单效果
- react提供了一个用于创建react项目的脚手架库: create-react-app
- 项目的整体技术架构为: react + webpack + es6 + eslint
- 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化
第一步,全局安装:npm i -g create-react-app
第二步,切换到想创项目的目录,使用命令:create-react-app hello-react
第三步,进入项目文件夹:cd hello-react
第四步,启动项目:npm start
public ---- 静态资源文件夹
favicon.icon ------ 网站页签图标
index.html -------- 主页面
logo192.png ------- logo图
logo512.png ------- logo图
manifest.json ----- 应用加壳的配置文件
robots.txt -------- 爬虫协议文件
src ---- 源码文件夹
App.css -------- App组件的样式
App.js --------- App组件
App.test.js ---- 用于给App做测试
index.css ------ 样式
index.js ------- 入口文件
logo.svg ------- logo图
reportWebVitals.js
--- 页面性能分析文件(需要web-vitals库的支持)
setupTests.js
---- 组件单元测试的文件(需要jest-dom库的支持)
| 1. 拆分组件: 拆分界面,抽取组件 2. 实现静态组件: 使用组件实现静态页面效果 3. 实现动态组件 3.1 动态显示初始化数据 3.1.1 数据类型 3.1.2 数据名称 3.1.2 保存在哪个组件? 3.2 交互(从绑定事件监听开始) |
todoList案例相关知识点
1.拆分组件、实现静态组件,注意:className、style的写法
2.动态初始化列表,如何确定将数据放在哪个组件的state中?
——某个组件使用:放在其自身的state中
——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
3.关于父子之间通信:
1.【父组件】给【子组件】传递数据:通过props传递
2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
4.注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value
5.状态在哪里,操作状态的方法就在哪里
父组件给子组件传递数据


父组件先给子组件传递一个函数,子组件再给父组件传递数据的时候,调用下这个函数即可



- React本身只关注于界面, 并不包含发送ajax请求的代码
- 前端应用需要通过ajax请求与后台进行交互(json数据)
- react应用中需要集成第三方ajax库(或自己封装)
- jQuery: 比较重, 如果需要另外引入不建议使用
- axios: 轻量级, 建议使用
- 封装XmlHttpRequest对象的ajax
- promise风格
- 可以用在浏览器端和node服务器端
GET请求

POST请求

1.工具库: PubSubJS
2.下载: npm install pubsub-js --save
3.使用:
1)import PubSub from 'pubsub-js' //引入
2)PubSub.subscribe('delete', function(data){ }); //订阅
3)PubSub.publish('delete', data) //发布消息

1.单页Web应用(single page web application,SPA)。
2.整个应用只有一个完整的页面。
3.点击页面中的链接不会刷新页面,只会做页面的局部更新。
4.数据都需要通过ajax请求获取, 并在前端异步展现。
7.1.2. 路由的理解
1.什么是路由?
1.一个路由就是一个映射关系(key:value)
2.key为路径, value可能是function或component
2.路由分类
后端路由:
1)理解: value是function, 用来处理客户端提交的请求。
2)注册路由: router.get(path, function(req, res))
3)工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
前端路由:
1)浏览器端路由,value是component,用于展示页面内容。
2)注册路由:
3)工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件
7.1.3. react-router-dom的理解
注意安装的是npm add react-router-dom@5.2.0

1.react的一个插件库。
2.专门用来实现一个SPA应用。
3.基于react的项目基本都会用到此库。
1.
2.
3.
4.
5.
6.
7.
7.2.2. 其它(路由组件才有下面的的对象)
1.history对象
2.match对象
3.withRouter函数
1.明确好界面中的导航区、展示区
2.导航区的a标签改为Link标签
Demo
3.展示区写Route标签进行路径的匹配
4.的最外侧包裹了一个 或
1.写法不同:
一般组件:
路由组件:
2.存放位置不同:
一般组件:components
路由组件:pages
3.接收到的props不同:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性
history:
go: ƒ go(n)
goBack: ƒ goBack()
goForward: ƒ goForward()
push: ƒ push(path, state)
replace: ƒ replace(path, state)
location:
pathname: "/about"
search: ""
state: undefined
match:
params: {}
path: "/about"
url: "/about"
1.默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
2.开启严格匹配:
3.严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由{/* 注册路由 */}
1.一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
2.具体编码:
}/>
}/> {/* 首次进入页面是重定向到/home */}
}>
7.7Switch的使用
1.通常情况下,path和component是一一对应的关系。
2.Switch可以提高路由匹配效率(单一匹配)。
1.默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
2.开启严格匹配:
3.严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
1.注册子路由时要写上父路由的path值
2.路由的匹配是按照注册路由的顺序进行的
第一种:params传参
路由链接(携带参数):
详情
注册路由(声明接收):
接收参数:this.props.match.params
Detail
Detail}/>
第二种search参数
路由链接(携带参数):
详情
注册路由(无需声明,正常注册即可):
接收参数:this.props.location.search
路由链接(携带参数):
{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情
注册路由(无需声明,正常注册即可):
接收参数:this.props.location.state
备注:刷新也可以保留住参数
借助this.prosp.history对象上的API对操作路由跳转、前进、后退
-this.prosp.history.push()
-this.prosp.history.replace()
-this.prosp.history.goBack()
-this.prosp.history.goForward()
-this.prosp.history.go()
withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
withRouter的返回值是一个新组件
8.1.1. material-ui(国外)
- 1.官网: http://www.material-ui.com/#/
- 2.github: https://github.com/callemall/material-ui
8.1.2. ant-design(国内蚂蚁金服)
- 1.官网: https://ant.design/index-cn
- 2.Github: https://github.com/ant-design/ant-design/
9.1.1. 学习文档
- 1.英文文档: https://redux.js.org/
- 2.中文文档: http://www.redux.org.cn/
- 3.Github: https://github.com/reactjs/redux
1.redux是一个专门用于做状态管理的JS库(不是react插件库)。
2.它可以用在react, angular, vue等项目中, 但基本与react配合使用。
3.作用: 集中式管理react应用中多个组件共享的状态。
1.某个组件的状态,需要让其他组件可以随时拿到(共享)。
2.一个组件需要改变另一个组件的状态(通信)。
3.总体原则:能不用就不用, 如果不用比较吃力才考虑使用。

1.动作的对象
2.包含2个属性
type:标识属性, 值为字符串, 唯一, 必要属性
data:数据属性, 值类型任意, 可选属性
3.例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }
9.2.2 reducer
1.用于初始化状态、加工状态。
2.加工时,根据旧的state和action, 产生新的state的纯函数。
1.将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时, 自动调用
9.3.1 createstore()
作用:创建包含指定reducer的store对象
1.作用: 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)
作用:应用上基于redux的中间件(插件库)
作用:合并多个reducer函数
| ## 1.求和案例_redux精简版 (3).store.js: (4).count_reducer.js: (5).在index.js中监测store中状态的改变,一旦发生改变重新渲染
## 3.求和案例_redux异步action版 ## 4.求和案例_react-redux基本使用
## 6.求和案例_react-redux数据共享版 ## 7.求和案例_react-redux开发者工具的使用 ## 8.求和案例_react-redux最终版 |
1.redux默认是不能进行异步处理的,
2.某些时候应用中需要在redux中执行异步任务(ajax, 定时器)
npm install redux-thunk

1.一个react插件库
2.专门用来简化react应用中使用redux
9.6.2. react-Redux将所有组件分成两大类
1.UI组件
1)只负责 UI 的呈现,不带有任何业务逻辑
2)通过props接收数据(一般数据和函数)
3)不使用任何 Redux 的 API
一般保存在components文件夹下
1)负责管理数据和业务逻辑,不负责UI的呈现
2)使用 Redux 的 API
一般保存在containers文件夹下
1.Provider:让所有组件都可以得到state数据
2.connect:用于包装 UI 组件生成容器组件
3.mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
4.mapDispatchToProps:将分发action的函数转换为UI组件的标签属性

npm install redux-devtools-extension
1.一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)
2.必须遵守以下一些约束
1)不得改写参数数据
2)不会产生任何副作用,例如网络请求,输入和输出设备
3)不能调用Date.now()或者Math.random()等不纯的方法
3.redux的reducer函数必须是一个纯函数
9.8.2. 高阶函数
1.理解: 一类特别的函数
1)情况1: 参数是函数
2)情况2: 返回是函数
2.常见的高阶函数:
1)定时器设置函数
2)数组的forEach()/map()/filter()/reduce()/find()/bind()
3)promise
4)react-redux中的connect函数
作用: 能实现更加动态, 更加可扩展的功能
setState(stateChange, [callback])------对象式的setState
1.stateChange为状态改变对象(该对象可以体现出状态的更改)
2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用

(2). setState(updater, [callback])------函数式的setState
1.updater为返回stateChange对象的函数。
2.updater可以接收到state和props。
4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。

总结:
1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
(1).如果新状态不依赖于原状态 ===> 使用对象方式
(2).如果新状态依赖于原状态 ===> 使用函数方式
(3).如果需要在setState()执行后获取最新的状态数据, 要在第二个callback函数中读取
//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
// import Home from './Home'
// import About from './About'import Loading from './Loading'
const Home = lazy(()=> import('./Home') )
const About = lazy(()=> import('./About'))
//2.通过指定在加载得到路由打包文件前显示一个自定义loading界面
}>
{/* 注册路由 */}
(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性
(1). State Hook: React.useState()
(2). Effect Hook: React.useEffect()
(3). Ref Hook: React.useRef()
10.3.2.1State 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): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

10.3.2.2Effect Hook
(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用操作:
发ajax请求数据获取
设置订阅 / 启动定时器
手动更改真实DOM
(3). 语法和说明:
useEffect(() => {
// 在此可以执行任何带副作用操作
return () => { // 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
(4). 可以把 useEffect Hook 看做如下三个函数的组合
componentDidMount()
componentDidUpdate()
componentWillUnmount()
10.3.2.3Ref Hook
(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样
<>>
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
1) 创建Context容器对象:
const XxxContext = React.createContext()
2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
子组件
3) 后代组件读取数据://第一种方式:仅适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中的value数据
//第二种方式: 函数组件与类组件都可以
{
value => ( // value就是context中的value数据
要显示的内容
)
}
只要执行setState(),即使不改变状态数据, 组件也会重新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来优化
Vue中:
使用slot技术, 也就是通过组件标签体传入结构
React中:
使用children props: 通过组件标签体传入结构
使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
<A>
xxxx
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到
}>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data}
理解:
错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面
特点:
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
使用方式:
getDerivedStateFromError配合componentDidCatch
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
};
}componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}
组件间的关系:
父子组件
兄弟组件(非嵌套组件)
祖孙组件(跨级组件)
几种通信方式:
1.props:
(1).children props
(2).render props
2.消息订阅-发布:
pubs-sub、event等等
3.集中式管理:
redux、dva等等
4.conText:
生产者-消费者模式
比较好的搭配方式:
父子组件:props
兄弟组件:消息订阅-发布、集中式管理
祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)
React注意事项