第一章:React入门
React简介
官网
中文官网: https://react.docschina.org/
英文官网: https://reactjs.org/
介绍描述
用于动态构建用户界面的 JavaScript 库(只关注于视图)
由Facebook开源
React特点
声明式编码
组件化编码
React Native 编写原生应用
高效(优秀的Diffing算法)
React高效的原因
使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。
DOM Diffing算法, 最小化页面重绘。
React的基本使用
相关js库
react.js:React核心库。
react-dom.js:提供操作DOM的react扩展库。
babel.min.js:解析JSX语法代码转为JS代码的库
创建虚拟dom的两种方式
1.纯js方式(一般不使用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <!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>
|
2.jsx方式
<script type="text/babel" > /* 此处一定要写babel */
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <!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>
|
React JSX
jsx语法规则:
1.定义虚拟DOM时,不要写引号。
2.标签中混入JS表达式时要用{}。
3.样式的类名指定不要用class,要用className。
4.内联样式,要用style={{key:value}}
的形式去写。
5.只有一个根标签
6.标签必须闭合
7.标签首字母
(1).若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。
(2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <!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'))
/* jsx语法规则: 1.定义虚拟DOM时,不要写引号。 2.标签中混入JS表达式时要用{}。 3.样式的类名指定不要用class,要用className。 4.内联样式,要用style={{key:value}}的形式去写。 5.只有一个根标签 6.标签必须闭合 7.标签首字母 (1).若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。 (2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
*/ </script> </body> </html>
|
组件
函数式组件
组件名必须首字母大写
虚拟DOM元素只能有一个根元素
虚拟DOM元素必须有结束标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <!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(<MyComponent/>
…….之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
类式组件
类的基本知识复习:
1.类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
3.类中所定义的方法,都放在了类的原型对象上,供实例去使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>1_类的基本知识</title> </head> <body> <script type="text/javascript" >
class Person { constructor(name,age){ this.name = name this.age = age } speak(){ console.log(`我叫${this.name},我年龄是${this.age}`); } }
class Student extends Person { constructor(name,age,grade){ super(name,age) this.grade = grade this.school = '尚硅谷' } speak(){ console.log(`我叫${this.name},我年龄是${this.age},我读的是${this.grade}年级`); this.study() } study(){ console.log('我很努力的学习'); } } class Car { constructor(name,price){ this.name = name this.price = price } a = 1 wheel = 4 static demo = 100 } const c1 = new Car('奔驰c63',199) console.log(c1); console.log(Car.demo); </script> </body> </html>
|
类式组件:
执行了ReactDOM.render(<MyComponent/>
…….之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <!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>
|
组件实例的三大核心属性 1.state
理解
state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
组件被称为”状态机”, 通过更新组件的state来更新对应的页面显示(重新渲染组件)
强烈注意
组件中render方法中的this为组件实例对象
组件自定义的方法中this为undefined,如何解决?
(a) 强制绑定this: 通过函数对象的bind()
(b) 箭头函数
- 状态数据,不能直接修改或更新
state规范写法(仅供学习):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| <!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'); 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简单写法(开发常用):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <!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. .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(){D 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: props
效果
需求:自定义用来显示一个人员信息的组件
1. 姓名必须指定,且为字符串类型;
2. 性别为字符串类型,如果性别没有指定,默认为男
3. **年龄为字符串类型,且为数字类型,默认值为18 **
理解
每个组件对象都会有props(properties的简写)属性
组件标签的所有属性都保存在props中
作用
通过标签属性从组件外向组件内传递变化的数据
注意: 组件内部不要修改props数据
编码操作
内部读取某个属性值
对props中的属性值进行类型限制和必要性限制
第一种方式(React v15.5 开始已弃用):
1 2 3 4
| Person.propTypes = { name: React.PropTypes.string.isRequired, age: React.PropTypes.number }
|
第二种方式(新):使用prop-types库进限制(需要引入prop-types库)
1 2 3 4
| Person.propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number. }
|
扩展属性: 将对象的所有属性通过props传递
默认属性值:
1 2 3 4
| Person.defaultProps = { age: 18, sex:'男' }
|
组件类的构造函数
1 2 3 4
| constructor(props){ super(props) console.log(props) }
|
案例
props基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <!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>
|
props进行限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <!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的简写方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <!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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <!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>
|
组件三大核心属性 3: refs与事件处理
效果
需求: 自定义组件, 功能说明如下:
1. 点击按钮, 提示第一个输入框中的值
2. 当第2个输入框失去焦点时, 提示这个输入框中的值
![](https://qny.aqingya.cn/img/props_event (2).gif)
理解
组件内的标签可以定义ref属性来标识自己
编码
字符串形式的ref
回调形式的ref
1
| <input ref={(c)=>{this.input1 = c}}
|
createRef创建ref容器
1 2
| myRef = React.createRef() <input ref={this.myRef}/>
|
事件处理
- 通过onXxx属性指定事件处理函数(注意大小写)
(1) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
(2) React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
- 通过event.target得到发生事件的DOM元素对象
案例
字符串形式的ref
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <!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="点击按钮提示数据"/> <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>
|
回调函数形式的ref
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <!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> <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>
|
回调ref中回调执行次数的问题
如果 ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null
,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。 传送门
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <!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> {/*<input ref={(c)=>{this.input1 = c;console.log('@',c);}} type="text"/><br/><br/>*/} <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>
|
createRef的使用
React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <!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 a="1" b="2"/>,document.getElementById('test')) </script> </body> </html>
|
组件三大核心属性 3: refs与事件处理
事件处理
(1). 通过onXxx属性指定事件处理函数(注意大小写)
a. React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
b. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
(2). 通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <!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 a="1" b="2"/>,document.getElementById('test')) </script> </body> </html>
|
收集表单数据
效果
需求 定义一个包含表单的组件
输入用户名密码后点击登录提示输入信息
![](https://qny.aqingya.cn/img/收集表单数据 (2).gif)
理解
包含表单的组件分类
受控组件
非受控组件
案例
受控组件
总结:随取随用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <!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>
|
非受控组件
总结:实时将你更新的数据 同步到statu状态中,用的时候再去取,类似vue的双向绑定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| <!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>
|
组件的生命周期
效果
需求:定义组件实现以下功能:
1. 让指定的文本做显示/隐藏的渐变动画
2. 从完全可见,到彻底消失,耗时2S
3. 点击“不活了”按钮从界面中卸载组件
理解
组件从创建到死亡它会经历一些特定的阶段。
React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
生命周期流程图(旧)
生命周期的三个阶段(旧)
1.初始化阶段: 由ReactDOM.render()触发—初次渲染
constructor()
componentWillMount()
render() =====> 必须使用的一个
componentDidMount() // 常用:一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息。
**2.更新阶段:**由组件内部this.setSate()或父组件重新render触发
shouldComponentUpdate() // 阀门 如果不写会提供一个默认返回值为真;若写了,按自己写的为主,true为通过,false为不通过。
componentWillUpdate()
render()
componentDidUpdate()
**3.卸载组件:**由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() // 常用:一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息。
引出生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <!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>
|
react生命周期(旧)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| <!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>
|
生命周期流程图(新)
生命周期的三个阶段(新)
1.初始化阶段: 由ReactDOM.render()触发—初次渲染
constructor()
getDerivedStateFromProps
render()
componentDidMount()
2.更新阶段: 由组件内部this.setSate()或父组件重新render触发
getDerivedStateFromProps
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate
componentDidUpdate()
3.卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
重要的勾子
render:初始化渲染或更新渲染调用
componentDidMount:开启监听, 发送ajax请求
componentWillUnmount:做一些收尾工作, 如: 清理定时器
即将废弃的勾子
componentWillMount
componentWillReceiveProps
componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
虚拟DOM与DOM Diffing算法
基本原理图
React应用(基于React脚手架)
create-react-app创建react应用
脚手架
- 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
react脚手架项目结构
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、React并不会真正的绑定事件到每一个具体某个DOM元素上,而是采用事件代理的模式;