0%

ReactDay3—Optimisation

性能优化

为什么使用setState?

  • 修改了state之后,希望React根据最新的State来重新渲染界面,但是这种方式的修改React并不知道数据发生了变化

  • React并没有实现类似于Vue3中的Proxy的方式来监听数据的变化

  • 必须通过setState来告知React数据已经发生了变化

setState原理是使用一个Object.assign(this.state, newState)进行合并.

setState可以传入一个回调函数

  • 好处1:可以在回调函数中编写新的state的逻辑

  • 好处2:当前回调函数会将之前的state和props传递进来

1
2
3
4
5
6
7
8
9
10
changeText() {
this.setState((state, props) => {
console.log(this.state.message, this.props);
return {
message:"hello,oo"
}
})
}


setState异步更新

1
2
3
4
5
6
7
8
9
10
changeText() {
this.setState({message:"你好哦哦"})//并不会立刻更新message,
console.log("----",this.state.message);//此时还是constructor中的原message

//希望数据更新后,获取对应的结果执行一些逻辑代码
//那么可以设置setState中传入第二个参数,callback
this.setState({message:"hello"}, () => {
console.log("++",this.state.message);
})
}

为什么setState是异步的

  • 每次调用setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染
  • 最好的办法是获取到多个更新,知乎进行批量更新

如果同步更新了state但是还没有执行render函数,那么state和props不能保持同步

React18之前在setTimeout中setState操作是同步操作.,18之后就变成了批处理

React diff算法

复杂度O(n)

  • 同层节点之间相互比较,不会跨节点比较
  • 不同类型的节点,产生不同的树结构
  • 开发中,可以通过key来指定那些节点在不同的渲染下保持稳定

keys的作用 针对于列表元素

  • 在最后位置插入数据,此时有无key没有差别
  • 在前面插入数据,在没有key的情况下,所有的li都需要进行修改

当子元素拥有key时,react使用Key来匹配原有树上的子元素以及最新树上的子元素

使用注意:

  • key应该是唯一的
  • key不要使用随机数
  • 使用index作为key,对性能是没有优化的

render优化

父子组件嵌套时,修改一个子组件,其他很多组件没有必要重新render,他们调用render应该有个前提就是依赖的数据(state,props)发生改变时,再调用自己的render方法.

  • 控制render方法调用:shouldComponentUpdate
    • 该方法有两个参数; nextProps:修改之后 最新的props属性; nextState修改之后最新的state属性
1
2
3
4
5
6
shouldComponentUpdate(nextProps, newState) {
if(this.state.message !== newState.message) {
return true
}
return false
}

想省略shouldComponentUpdate,

  • 类组件继承PureComponent而不是component
  • 函数式组织采用memo将函数包裹起来

State数据不可变性

当this.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
export class App extends Component {
constructor() {
super()
this.state = {
books: [
{ name: "css", price:121, count:1},
{ name: "js", price:52, count:1},
{ name: "html", price:11, count:1},
{ name: "c++", price:99, count:1},
]
}
}

addNewMessag(index) {
const newBook = {name:"xxx", price:25, count:1}
//1.直接修改原有state,重新设置一遍,这对于继承自purecomponent的组件,无法引起重新渲染render
this.state.books.push(newBook)
this.setState({books:this.state.books})

//正确做法,复制一份原数据修改
const books = [...this.state.books]
books.push(newBook)
books[index].count++
this.setState({books:books})
}

Ref获取DOM和组件

ref属性就是与获取关联.

DOM组件

通常不需要直接操作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
export class App extends PureComponent {
constructor(){
super()
this.state = {

}
this.titleRef = createRef()
}
getNativeDOM() {
//方式1:在React元素绑定一个ref字符串
console.log(this.refs.why);
//方式2:提前创建好ref对象,creatRef(),将创建出的对象绑定到元素
console.log(this.titleRef.current);
//方式3:
console.log(this.titleEl);
}

render() {
return (
<div>
<h2 ref="why">xixi</h2>
<h2 ref={this.titleRef}>xixi</h2>
<h2 ref={el => { this.titleEl = el}}>ohuoohuo</h2>
<button onClick={e => this.getNativeDOM()}>获取DOM</button>
</div>
)
}
}

获取组件

类组件

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
class HelloWorld extends PureComponent {
test() {
console.log("子组件实例");
}
render() {
return <h1>hello world</h1>
}
}

export class App extends PureComponent {
constructor(){
super()
this.state = {
}

this.hwRef = createRef()
}

getComponent() {
console.log(this.hwRef.current);//获取实例对象
this.hwRef.current.test()
}

render() {
return (
<div>
<HelloWorld ref={this.hwRef} />
<button onClick={e => this.getComponent()}>获取组件实例</button>
</div>
)
}
}

函数组件 -forwardRef

比如要绑定函数组件返回的某一个元素

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
const HelloWorld = forwardRef(function(props, ref) {
return (
<div>
<h1 ref={ref}>111</h1>
<p>222</p>
</div>
)
})

export class App extends PureComponent {
constructor(){
super()
this.state = {
}

this.hwRef = createRef()
}
getComponent() {
console.log(this.hwRef.current);//获取实例对象
this.hwRef.current.test()
}

render() {
return (
<div>
<HelloWorld ref={this.hwRef} />
<button onClick={e => this.getComponent()}>获取组件实例</button>
</div>
)
}
}

受控组件

对于input, textare, select,等表单组件加上value(value与state中属性绑定了)后就变成受控组件,此时是无法向里面输入内容,必须加上onChange

  • 文本框
  • 单选框
  • 多选框
  • selected框

单选框,多选框,注意此时不是从value中拿值而是从checked中取

多选框注意先需要创建一个数组.记录各个元素情况

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
import React, { PureComponent } from 'react'

export class App extends PureComponent {
constructor() {
super()

this.state = {
username:'',
password:'',
isAgree:true,
hobbies:[
{value:"sing", text:"唱", isChecked:false},
{value:"dance", text:"跳", isChecked:false},
{value:"rap", text:"rap", isChecked:false},
],
fruit:["orange"]
}
}

handleSubmitClick(event) {
//1.阻止默认行为,避免表单向action对象发送请求引起页面刷新
event.preventDefault()
//获取到所有的表单数据,对数据进行组织
console.log(this.state.username);
console.log(this.state.password);
console.log(this.state.hobbies.filter(item => item.isChecked).map(item => item.value));
console.log(this.fruit);
//以网络请求的方式,将数据传递给服务器(ajax)
}

handleInputChange(event) {
const keyName = event.target.name
this.setState({
[keyName]: event.target.value
})
}

handleAgreeChange(event) {
this.setState({isAgree:event.target.checked})
}

handleHobbiesChange(event, index){
const hobbies = [...this.state.hobbies]
hobbies[index].isChecked = event.target.checked
this.setState({ hobbies })
}

handleFruitChange(event) {
const options = Array.from(event.target.selectedOptions)
const values = options.map(item => item.value)
this.setState({ fruit: values })
}


render() {
const { username,password,isAgree, hobbies, fruit} = this.state

return (
<div>
<form onSubmit={e => this.handleSubmitClick(e)}>
//文本框
<label htmlFor="username">
用户: <input id='username' type='text' value={username} name='username' onChange={e => this.handleInputChange(e)}/>
</label>
<label htmlFor="password">
密码: <input id='password' type='password' value={password} name='password' onChange={e => this.handleInputChange(e)}/>
</label>

//单选框
<label htmlFor="agree">
<input id="agree" type="checkbox" checked={isAgree} onChange={e => this.handleAgreeChange(e)}/>同意协议
</label>

//多选框
<div>
您的爱好
{
hobbies.map((item,index) => {
return (
<label htmlFor={item.value} key={item.value}>
<input type="checkbox" id={item.value} checked={item.isChecked} onChange={e => this.handleHobbiesChange(e, index)}/>
<span>{item.text}</span>
</label>
)
})
}
</div>
//勾选框
<select value={ fruit } onChange={e => this.handleFruitChange(e)} multiple>
<option value="apple">apple</option>
<option value="banana">banana</option>
<option value="orange">orange</option>
</select>

<button type='submit'>提交</button>
</form>
</div>
)
}
}

export default App
-------------本文结束感谢您的阅读-------------