性能优化
为什么使用setState?
修改了state之后,希望React根据最新的State来重新渲染界面,但是这种方式的修改React并不知道数据发生了变化
React并没有实现类似于Vue3中的Proxy的方式来监听数据的变化
必须通过setState来告知React数据已经发生了变化
setState原理是使用一个Object.assign(this.state, newState)进行合并.
setState可以传入一个回调函数
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:"你好哦哦"}) console.log("----",this.state.message);
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} 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() { console.log(this.refs.why); console.log(this.titleRef.current); 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
单选框,多选框,注意此时不是从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) { 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); }
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
|