生命周期
生命周期类似如下代码:
let div = document.createElement('div')
//这是div的create/construct过程
div.textContent = 'hi'
// 这是初始化state
document.body.appendChild(div)
// 这是div的mount过程
div.textContext = 'hi2'
// 这是div的update过程
div.remove()
// 这是div的unmount过程
React组件也有这些过程,我们称为生命周期。
我们主要了解以下几个生命周期:
constructor()——在这里初始化state
shouldComponentUpdate()——return false 阻止更新
render()——创建虚拟DOM
componentDidMount()——组件已出现在页面
componentDidUpdate()——组件已更新
componentWillUnmount()——组件将死
constructor
用途
- 用于初始化props
- 初始化state,但此时不能调用setState
- 用来写bind this,如下:
constructor(){
/*其他代码略*/
this.onClick = this.onClick.bind(this)
}
// 用新语法代替:
onClick = () => {}
constructor(){/*其他代码略*/}
- 若不初始化props和state,constructor也可不写
shouldComponentUpdate
用途
- 返回true表示不阻止UI更新
- 返回false表示阻止UI更新
问: shouldComponentUpdate的作用?
答: 它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活的设置返回值,以避免不必要的更新。
我们来个例子吧,结合例子和以上内容会更容易理解,情景如下:
我们定义一个函数onClick,对n进行+1和-1操作,如此,页面上的n是不会变的,那每次调用onClick会重新render吗?答案是会的。代码如下:
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
n :1
};
}
onClick = () => {
this.setState(state => ({
n: state.n+1
}));
this.setState(state => ({
n: state.n-1
}))
};
render(){
console.log('render了一次')
return (
<div>
{this.state.n}
<button onClick={this.onClick}>+1-1</button>
</div>
)
}
}
渲染的结果如下(注意看log)
那如何让页面上的值不变就不会重新render呢? 用sholdComponentUpdate
,将newState和this.state的每个属性都对比一下,如果全部相等就不更新,如果有一个不相等,就更新
constructor(props) {
super(props);
this.state = {
n:1
}
}
onClick = ()=>{
this.setState(state=>({n:state.n+1}))
this.setState(state=>({n:state.n-1}))
}
shouldComponentUpdate(newProps,newState){
if(newState.n === this.state.n){
return false
}else{
return true
}
}
render(){
console.log('render了一次');
return (
<div>
{this.state.n}
<button onClick={this.onClick}>+1-1</button>
</div>
)
}
}
结果是:不更新就不重新render,结果如下:
基于此特点,React内置了这个功能,这个功能叫React.PureComponet
来代替React.Component,可以实现和sholdComponentUpdate
一样的功能,代码如下:
export default class App extends React.PureComponent{
constructor(props){
super(props);
this.state={
n:1
}
}
onClick = () =>{
this.setState(state=>({n:state.n+1}))
this.setState(state=>({n:state.n-1}))
}
render(){
console.log('render了');
return(
<div>
{this.state.n}
<button onClick={this.onClick}>+1-1</button>
</div>
)
}
}
render
用途
- 用于展示视图:
return (<div>...</div>)
- 只能有一个根元素
- 如有两个根元素,用<React.Fragment>包起,<React.Fragment/>可缩写为<></>
技巧 - render里面可以写if...else
- render里可以写?:表达式
- render里不能直接写for循环,需要用数组,如下:
export default class App extends React.Component{
constructor(props){
super(props)
this.state = {
n:1,
array: [1,2,3,4,5]
}
}
render(){
let result = []
for (let i=0;i<this.state.array.length;i++){
result.push(this.state.array[i])
}
return result
}
}
- render里可以写array.map(循环)
class App extends React.Component{
constructor(props){
super(props)
this.state = {
n:1,
array: [1,2,3,4,5]
}
}
render(){
return this.state.array.map(n=><span key={n}>{n}</span>)
}
}
export default App;
componentDidMount()
用途
- 在元素插入页码后执行代码,这些代码依赖DOM
- 比如想要获取div的高度,最好在这里写
- 此处可以发起加载数据的AJAX请求(官方推荐)
- 首次渲染会执行此钩子
componentDidUpdate()
用途
- 视图更新后执行代码
- 此处也可以发起AJAX请求,用于更新数据
- 首次更新不会执行此钩子
- 在此处setState可能会引起无限循环,除非放在if里
- 若shouldComponentUpdate返回false,则不会触发此钩子
componentWillUnmount
用途
- 组件将要被移出页面然后被销毁时执行代码
- unmount过的组件不会再次mount
举例 - 如果在componentDidMount里面监听了window.scroll,那么就要在componentWillUnmount里面取消监听
- 如果在componentDidMount里面创建了Timer,那么就要在componentWillUnmount里面取消Timer
- 如果在componentDidMount里面创建了AJAX请求,那么就要在componentWillUnmount里面取消AJAX请求
最后通过一张图回顾各钩子执行顺序