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<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
// 1.创建函数式组件【组件:包含html,js,css等等】
function MyComponent(){
console.log(this); // undefined babel编译后,开启了严格模式
return <h2>我使用函数定义的组件(适用于【简单函数】的定义)</h2>
}
// 2.渲染组件到页面
// 不能写函数名【写标签,开头大写,标签闭合】
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
/*
执行了ReactDOM.render() .........之后,发生了什么?
1.React解析组件标签,找到 MyComponent 组件
2.发现组件是用函数定义的,调用该组件,将返回的虚拟DOM转为真实的DOM,随后呈现在页面中
*/
</script>
</body>
</html> - 类式组件
- 类的基本使用
- 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写
- 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的
- 类中所定义的方法,都是放在了类的原型对象上,供实例去使用
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// 定义一个类
class Person{
// 构造器
constructor(name,age){
// this是类的实例对象【lisi】
this.name = name
this.age = age
}
speak(){
// 通过Person实例调用speak,speak中的this就是Person实例
console.log(`${this.name}在说话`); ————类的原型对象上,供实例使用
}
info(){
console.log(`我的名字${this.name},今年${this.age}`);
}
}
class Student extends Person{
constructor(name,age,grade){
super(name,age)
this.grade = grade
}
info(){
console.log(`我的名字${this.name},今年${this.age},${this.grade}`);
}
}
let s1 = new Student('小明',23)
let s2 = new Student('李四',20,'大一')
s1.info()
s1.speak()
s2.info()
s2.speak()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<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
// 1.创建类式组件
class MyComponent extends React.Component {
render(){
// render是放在哪里的? ————MyComponent的原型对象上,供实例使用
// render中的this是谁? ————MyComponent的实例对象【MyComponent组件实例对象】
console.log(123,this);
return <h2>我使用类定义的组件(适用于【复杂函数】的定义)</h2>
}
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
/*
执行了ReactDOM.render() .........之后,发生了什么?
1.React解析组件标签,找到 MyComponent 组件
2.发现组件是用类定义的,随后 new 出来该类【MyComponent】的实例,通过该实例调用原型上的方法【render】
3.将 render 返回的虚拟DOM转为真实的DOM,随后呈现在页面中
*/
</script>
</body>
</html>
- 类的基本使用
组件实例【类式组件】三大核心属性:state【状态】
state是组件对象最重要的属性,值是对象
组件被称为 “状态机” ,通过更新组件的 state 来更新对应的页面(重新渲染组件)
- 组件中 render 的this为组件实例对象
- 组件中的自定义方法的 this 为undefined【解决:1.bind()方法 2.箭头函数】
- 状态数据不能直接修改和更新
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<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<button id="btn3" onclick="demo()">按钮3</button>
<script type="text/javascript">
const btn1 = document.getElementById('btn1')
btn1.addEventListener('click',()=> {
alert('按钮1被点击')
})
btn2.onclick = () => {
alert('按钮2被点击')
}
function demo(){
alert('按钮3被点击')
}
// 类中的实例调用 与 直接调用
class Person {
constructor(name,age){
this.name = name
this.age = age
}
speak(){
console.log(this);
}
}
let p1 = new Person('小明',18)
// 实例调用
p1.speak() // Person的实例对象
// 直接调用
let x = p1.speak
console.log('直接调用',x); // 函数
// undefined 严格模式下this为undefined
// 不开启this指向window
function text(){
// 'use strict'
console.log(this);
}
text()
</script>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<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class Weather extends React.Component {
// 构造器调用几次? ————————1次 【只用了一次Weather标签,只new出来一个实例对象】
// 构造器的作用:1.初始化状态 2.修改this指向
constructor(props) {
console.log('constructor');
super(props)
// 初始化状态
this.state = { isHot: false ,wind:'微风'}
// bind改变this的指向,解决changeWeather中的this指向问题
this.changeWeather = this.changeWeather.bind(this)
}
// render调用几次? ————————1+n次【每次修改状态,就会render一次(1是初识渲染,n是状态更新次数)】
// render的作用:1.从状态里读取数据 2.根据状态值做展示
render() {
console.log('render');
const { isHot,wind } = this.state
return (
<h1>今天天气很<span onClick={this.changeWeather}>{isHot ? '炎热' : '寒冷'},{wind}</span></h1>
)
}
// 由于 changeWeather 是作为 onClick 的回调,所以不是通过实例调用的,是直接调用
// 类中的方法默认开启了局部的严格模式
// changeWeather() { console.log(this); } // 为什么demo里的this是undefined ? ————————Weather实例没有调用demo
// render调用几次? ————————点几次,调几次
changeWeather() {
// 通过 Weather 实例调用 changeWeather 时,changeWeather中的this就是Weather实例
// 注意: React.js不允许直接更改 state 里的数据 【例如: this.state.isHot = !this.state.isHot】
// 注意: 状态必须通过 setState 进行更改,且更新是一种合并,以前的数据不会丢失
this.setState({
isHot:!this.state.isHot
})
}
}
ReactDOM.render(<Weather />, document.getElementById('app'))
</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<div id="app"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script 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>今天天气很<span onClick={this.changeWeather}>{isHot ? '炎热' : '寒冷'},{wind}</span></h1>
)
}
// 自定义方法
changeWeather = () => {
this.setState({
isHot: !this.state.isHot
})
}
}
ReactDOM.render(<Weather />, document.getElementById('app'))
</script>
组件实例【类式组件】三大核心属性:props
基本使用
1 | <div id="app"></div> |
扩展运算符
1 | <script type="text/javascript"> |
对props进行限制
- 添加类的规则写法:类名.propTypes = {} 例如:Person.propTypes = {id: PropTypes.number.isRequired}
- 默认的标签属性值写法:类名.defaultProps = {}
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<div id="app"></div>
<div id="app1"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component {
render() {
let { name, gender, age } = this.props
return (
<div >
<ul>
<li>姓名:{name}</li>
<li>性别:{gender}</li>
<li>年龄:{age + 1}</li>
</ul>
</div>
)
}
}
// 添加类的规则【对标签属性进行类型、必要性的限制】
// isRequired必传 defaultProps默认值
Person.propTypes = {
name: PropTypes.string.isRequired,
gender: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func,
}
// 指定默认的标签属性值
Person.defaultProps = {
gender: '男',
age: 18
}
let data = {
name: '12',
gender: '男',
age: 18
}
// {...data} 的花括号是作为分割符在使用 相当于 ...data
ReactDOM.render(<Person name='222' gender='女' age={18} speak={speak}/>, document.getElementById('app'))
ReactDOM.render(<Person {...data} />, document.getElementById('app1'))
function speak(){
console.log('说话了');
}
</script>
简写props
1 | <div id="app"></div> |
构造器的注意事项
- 构造器是否接收 props ,是否传递给 super ,取决于:是否希望在构造器中通过this访问props
- 当 constructor 不传 props,super也不接收 props ,通过实例访问props【this.props】 则为 undefined
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<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component {
// constructor 尽量不写构造器
constructor(props){
// 构造器是否接收 props ,是否传递给 super ,取决于:是否希望在构造器中通过this访问props
super(props)
// 1.当 constructor 不传 props,super也不接收 props ,通过实例访问props【this.props】 则为 undefined
console.log(props);
}
// 给Person自身添加一个属性 propTypes
// 添加类的规则【对标签属性进行类型、必要性的限制】
// isRequired必传 defaultProps默认值
static propTypes = {
name: PropTypes.string.isRequired,
gender: PropTypes.string,
age: PropTypes.number
}
// 给Person自身添加一个属性 defaultProps
// 指定默认的标签属性值
static defaultProps = {
gender: '男',
age: 18
}
render() {
let { name, gender, age } = this.props
// this.props.name = 'Tom' // 报错 props是只读的
return (
<div >
<ul>
<li>姓名:{name}</li>
<li>性别:{gender}</li>
<li>年龄:{age + 1}</li>
</ul>
</div>
)
}
}
ReactDOM.render(<Person name='222' />, document.getElementById('app'))
</script>
函数式组件只能使用 props
1 | <div id="app"></div> |
组件实例【类式组件】三大核心属性:refs 与事件处理
- refs理解: 组件内的标签可以定义 ref 来标识自己
- 字符串类型的ref写法: 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<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
// 类式组件
class Person extends React.Component {
render() {
let { name } = this.props
return (
<div>
<input ref='input' placeholder='点击按钮提示数据' type="text"/>
<button ref='btn1' onClick={this.open}>按钮</button>
<input ref='input1' placeholder='失去焦点提示数据' type="text" onBlur={this.showData}/>
</div>
)
}
open = () => {
// 是真实节点DOM,而不是虚拟DOM
// console.log(this.refs.input);
let {input} = this.refs
alert(input.value)
}
showData = () => {
let {input1} = this.refs
alert(input1.value)
}
}
ReactDOM.render(<Person name='lisi' />, document.getElementById('app'))
</script> - 回调函数形式的ref写法: ref={c => {this.input1 = c}} 【内联函数的方式】
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<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component {
render() {
let { name } = this.props
return (
<div>
<input ref={c => this.input = c} placeholder='点击按钮提示数据' type="text" />
<button ref='btn1' onClick={this.open}>按钮</button>
<input ref={c => this.input1 = c} placeholder='失去焦点提示数据' type="text" onBlur={this.showData} />
</div>
)
}
open = () => {
let { input } = this
alert(input.value)
}
showData = () => {
let { input1 } = this
alert(input1.value)
}
}
ReactDOM.render(<Person name='lisi' />, document.getElementById('app')) - 内联函数的方式:回调ref中回调次数的问题【解决:类绑定函数的方式:ref={this.textFnc}}】
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<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
// 回调ref中回调次数的问题
class Person extends React.Component {
state = {
isHot: false
}
open = () => {
let { input } = this
alert(input.value)
}
ChangeWeather = () => {
this.setState({
isHot: !this.state.isHot
})
}
// 通过将 ref 的回调函数定义成 class 的绑定函数的方式
// 不会再重复调用saveInput
saveInput = (c) => {
this.input = c
console.log("123",c)
}
render() {
let { isHot } = this.state
let { name } = this.props
// c页面更新后会调用连词,第一次为null,第二次才会返回节点
// jsx注释 {/* */}
// 通过将 ref 的回调函数定义成 class 的绑定函数的方式
return (
<div>
<h2>今天天气很{isHot ? '炎热' : '寒冷'}</h2>
{/* 内联用法比较多,即下面的方式 */}
{/* <input ref={c => {this.input = c ;console.log("123",c)}} placeholder='点击按钮提示数据' type="text" /> */}
<input ref={this.saveInput} placeholder='点击按钮提示数据' type="text" />
<button onClick={this.open}>按钮</button>
<button onClick={this.ChangeWeather}>改变天气</button>
</div>
)
}
}
ReactDOM.render(<Person name='lisi' />, document.getElementById('app'))
</script> - createRef的方式 【最推荐的方式】
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<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
// React.createRef ;调用后可以返回一个容器,该容器可以存储被 ref 所标识的节点 '该容器是专人专用'
class Person extends React.Component {
myRef = React.createRef()
myRef1 = React.createRef()
open = () => {
alert(this.myRef.current.value)
}
open1 = () => {
alert(this.myRef1.current.value)
}
render() {
return (
<div>
<input ref={this.myRef} placeholder='点击按钮提示数据' type="text" />
<button onClick={this.open}>按钮</button>
<input onBlur={this.open1} ref={this.myRef1} placeholder='失去焦点提示数据' type="text" />
</div>
)
}
}
ReactDOM.render(<Person name='lisi' />, document.getElementById('app'))
</script> - 事件处理
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<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component {
/*
1.通过 onXxx 属性指定事件处理函数 (注意大小写)
a. React使用的是自定义(合成)事件,而不是原生DOM事件 ——————为了更好的兼容性
b. React中的事件是通过 事件委托的方式 处理的(委托给组件最外层的元素) ——————为了高效
2.通过 event.target 得到发生事件的DOM元素对象 【发生事件的元素正好是你要操作的元素 】 ——————不要过度的使用ref
*/
// 创建容器
myRef = React.createRef()
myRef1 = React.createRef()
open = () => {
alert(this.myRef.current.value)
}
// 发生事件的元素正好是你要操作的元素
open1 = (e) => {
alert(e.target.value)
}
render() {
return (
<div>
<input ref={this.myRef} placeholder='点击按钮提示数据' type="text" />
<button onClick={this.open}>按钮</button>
<input onBlur={this.open1} placeholder='失去焦点提示数据' type="text" />
</div>
)
}
}
ReactDOM.render(<Person name='lisi' />, document.getElementById('app'))
</script>
收集表单数据 【受控组件、非受控组件】
- 非受控组件: 现用现取 【页面中输入类的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<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
/* 非受控组件 ; 现用现取 【页面中输入类的DOM,你是现用现取就是非受控组件】 */
// 创建组件
class Login extends React.Component {
handleSubmit = (e) => {
e.preventDefault() // 阻止默认事件
let { username, password } = this
alert(`用户名:${username.value},密码:${password.value}` )
}
render() {
return (
<form action="http://www.wuqisuda.cn" onSubmit={this.handleSubmit}>
用户名: <input ref={c => this.username = c} name="username" type="text" />
密码: <input ref={c => this.password = c} name="password" type="password" />
<button onClick={this.open}>登录</button>
</form>
)
}
}
// 渲染组件
ReactDOM.render(<Login name='lisi' />, document.getElementById('app'))
</script> - 受控组件: 页面中输入类的DOM,随着你的输入,就能把数据维护到状态里面去【类似 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<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
/* 受控组件 ; 现用现取 【页面中输入类的DOM,随着你的输入,就能把数据维护到状态里面去】【类似 vue 的 双向绑定】 */
// 创建组件
class Login extends React.Component {
// 初始化
state = {
username: '',
password: ''
}
saveUsername = (e) => {
this.setState({
username: e.target.value
})
}
savePassword = (e) => {
this.setState({
password: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault() // 阻止默认事件
let { username, password } = this.state
alert(`用户名:${username},密码:${password}`)
}
render() {
return (
<form action="http://www.wuqisuda.cn" onSubmit={this.handleSubmit}>
用户名: <input onChange={this.saveUsername} name="username" type="text" />
密码: <input onChange={this.savePassword} name="password" type="password" />
<button onClick={this.open}>登录</button>
</form>
)
}
}
// 渲染组件
ReactDOM.render(<Login name='lisi' />, document.getElementById('app'))
</script>
高阶函数 函数柯里化
- 高阶函数: 符合一下2个规范中的任意一个,就是高阶函数
- 若A函数,接收的参数是一个函数,A就是高阶函数
- 若A函数,调用的返回值是一个函数,A就是高阶函数
- 常见的高阶函数: Promise 、 setTimeout 、 arr.map()等
- 函数柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
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// 创建组件
class Login extends React.Component {
// 初始化
state = {
username: '',
password: ''
}
// saveFormdata接收一个 type 参数 返回一个函数 ,该函数接收一个 event 参数。在 this.setState 统一处理 【函数柯里化】
saveFormdata = (type) => {
// 将 saveFormdata 返回箭头函数给 onChange 作为回调
// 输入用户名和密码给的是 return 的函数,所以 saveFormdata 函数没有 event
// 输入后触发 onChange 事件,React 把 event 传给了箭头函数
return (event) => {
this.setState({
[type]:event.target.value
})
}
}
handleSubmit = (e) => {
e.preventDefault() // 阻止默认事件
let { username, password } = this.state
alert(`用户名:${username},密码:${password}`)
}
render() {
return (
<form action="http://www.wuqisuda.cn" onSubmit={this.handleSubmit}>
用户名: <input onChange={this.saveFormdata('username')} name="username" type="text" />
密码: <input onChange={this.saveFormdata('password')} name="password" type="password" />
<button onClick={this.open}>登录</button>
</form>
)
}
}
// 渲染组件
ReactDOM.render(<Login name='lisi' />, document.getElementById('app'))1
2
3
4
5
6
7
8
9
10
11
12<script type="text/javascript">
// 函数柯里化
function sum(a) {
return (b) => {
return (c) => {
return a + b + c
}
}
}
let result = sum(1)(2)(3)
console.log(result);
</script> - 不使用柯里化实现【内联函数】
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// 不使用柯里化函数实现
class Login extends React.Component {
// 初始化
state = {
username: '',
password: ''
}
saveFormdata = (type, event) => {
this.setState({
[type]: event.target.value
})
}
handleSubmit = (e) => {
e.preventDefault() // 阻止默认事件
let { username, password } = this.state
alert(`用户名:${username},密码:${password}`)
}
render() {
return (
<form action="http://www.wuqisuda.cn" onSubmit={this.handleSubmit}>
用户名: <input onChange={event => this.saveFormdata('username',event)} name="username" type="text" />
密码: <input onChange={event => this.saveFormdata('password',event)} name="password" type="password" />
<button onClick={this.open}>登录</button>
</form>
)
}
}
ReactDOM.render(<Login name='lisi' />, document.getElementById('app')) - 对象的相关知识
1
2
3
4
5
6
7
8<script type="text/javascript">
// 在空对象里添加属性 name :'tom'
let a = 'name'
let obj = {}
// 注意错误示例:obj.a = 'tom' // {a :'tom'}
obj[a] = 'tom'
console.log( obj); // {name :'tom'}
</script>
React 的 生命周期
1 | <div id="app"></div> |
React 的 生命周期 [旧]
- 初始化阶段:由ReactDOM.render()触发 ——————初次渲染
- constructor ————构造器【数据初始化(state)】
- componentWillMount ————将要挂载
- render ————渲染
- componentDidMount ————挂载完毕【常用】
- 做一些初始化的事情:发起网络请求、开启定时器、订阅消息
- 更新阶段: 由组件内部 this.setState() 或父组件重新 render 触发【强制更新 this.forceUpdate() : 强制更新数据钩子 ——————不走阀门】
- shouldComponentUpdate ————控制更新的阀门
- componentWillUpdate ————将要更新
- render ————渲染
- componentDidUpdate ————更新完毕
- 卸载组件:由ReactDOM.unmountComponentAtNode(document.getElementById(‘’))触发
- componentWillUnmount ————将要卸载
- 做一些收尾的事情:关闭定时器、取消订阅消息
- componentWillUnmount ————将要卸载
- 父组件更新render:
- componentWillReceiveProps ————组将将要接受props 【第一次接受的props不会触发componentWillReceiveProps钩子】
- shouldComponentUpdate
- componentWillUpdate
- render ————子组件渲染
- componentDidUpdate
17.0.0以后 componentWillReceiveProps componentWillMount componentWillUpdate前面要加 UNSAFE_ 【即将废弃】
1 | <div id="app"></div> |
React 的 生命周期 [新]
- 初始化阶段:由ReactDOM.render()触发 ——————初次渲染
- constructor ————构造器【数据初始化(state)】
- getDerivedStateFromProps ————从 props 得到一个派生的状态
- render ————渲染【常用】
- componentDidMount ————挂载完毕【常用】
- 做一些初始化的事情:发起网络请求、开启定时器、订阅消息
- 更新阶段: 由组件内部 this.setState() 或父组件重新 render 触发【强制更新 this.forceUpdate() : 强制更新数据钩子 ——————不走阀门】
- getDerivedStateFromProps ————从 props 得到一个派生的状态
- shouldComponentUpdate ————控制更新的阀门
- render ————渲染
- getSnapshotBeforeUpdate ————在更新之前得到快照
- componentDidUpdate ————更新完毕
- 卸载组件:由ReactDOM.unmountComponentAtNode(document.getElementById(‘’))触发
- componentWillUnmount ————将要卸载【常用】
- 做一些收尾的事情:关闭定时器、取消订阅消息
- componentWillUnmount ————将要卸载【常用】
- static getDerivedStateFromProps(){} : 从 props 得到一个派生的状态
- 此方法适用于罕见的用例,state 的值在任何时候都取决于 props
- 必须有返回值 state对象 或者 null
- getSnapshotBeforeUpdate : 在更新之前得到快照
- 必须有返回值 snapshot值【快照值】 或者 null
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<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
//#region
// 生命周期回调函数 == 生命周期钩子函数
//#endregion
class Life extends React.Component {
// 构造器
constructor(props) {
console.log('constructor');
super(props)
this.state = {
num: 0,
}
}
// 卸载组件按钮的回调
unMount = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
// 正常更新数据按钮的回调
upData = () => {
let { num } = this.state
num += 1
this.setState({
num
})
}
// forceUpdate()
// 强制更新数据钩子 ——————不走阀门
// 不对状态进行任何修改,更新一下
// 强制更新按钮的回调
force = () => {
this.forceUpdate()
}
// 从 props 得到一个派生的状态
// 必须有返回值 state对象 或者 null
// 此方法适用于罕见的用例,state 的值在任何时候都取决于 props
static getDerivedStateFromProps(props,state){
console.log('getDerivedStateFromProps',props,state);
return null
}
// 在更新之前得到快照
// 必须有返回值 snapshot值【快照值】 或者 null
getSnapshotBeforeUpdate(prevProps, prevState){
console.log('快照,getSnapshotBeforeUpdate',prevProps, prevState);
return '123123'
}
// 组件挂载完毕
componentDidMount() {
console.log('组件挂载完毕,componentDidMount');
}
// 组件将要卸载
componentWillUnmount() {
console.log('组件将要卸载,componentWillUnmount');
}
// 控制组件更新的阀门 ————————必有返回值【不去设置都是true】【布尔值】
shouldComponentUpdate() {
console.log('控制组件更新的阀门,shouldComponentUpdate');
return true
}
// 组件更新完毕
componentDidUpdate(preProps,preState,snapshotValue) {
console.log('组件更新完毕,componentDidUpdate',preProps,preState,snapshotValue);
}
render() {
console.log('渲染,render');
let { num } = this.state
return (
<div>
<h2>当前求和为{num}</h2>
<button onClick={this.upData}>点我加1</button>
<button onClick={this.unMount}>卸载组件</button>
<button onClick={this.force}>不更改状态中的数据,强制更新</button>
</div>
)
}
}
ReactDOM.render(<Life num={99} />, document.getElementById('app'))
</script>getSnapshotBeforeUpdate 应用场景:计算滚动位置
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<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo</title>
</head>
<style>
.list {
width: 200px;
height: 150px;
background-color: royalblue;
overflow-y: auto;
}
.news {
height: 30px;
}
</style>
<body>
<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
//#region
// 生命周期回调函数 == 生命周期钩子函数
//#endregion
class NewsList extends React.Component {
state = {
newsArr: [],
}
clear = () => {
clearInterval(this.timer)
}
componentDidMount() {
this.timer = setInterval(() => {
let { newsArr } = this.state
// 模拟一条新闻
let news = '新闻' + (newsArr.length + 1)
newsArr.unshift(news)
this.setState({
newsArr
})
}, 1000);
}
getSnapshotBeforeUpdate() {
return this.list.scrollHeight
}
componentDidUpdate(preProps, preState, snapshotValue) {
// this.list.scrollHeight 每次比 snapshotValue 多 30px
this.list.scrollTop += this.list.scrollHeight - snapshotValue
}
render() {
return (
<div>
<div className="list" ref={c => this.list = c}>
{
this.state.newsArr.map((item, index) => {
return <div key={index} className='news'>{item}</div>
})
}
</div>
<button onClick={this.clear}>清除定时器</button>
</div>
)
}
}
ReactDOM.render(<NewsList />, document.getElementById('app'))
</script>
</body>
</html>
- 必须有返回值 snapshot值【快照值】 或者 null
Diffing算法
验证Diffing算法
1 | class Time extends React.Component { |
key的作用
1 | <div id="app"></div> |