React类的生命周期

函数式组件只有属性,没有状态,更没有生命周期的概念,但是函数式组件在React16.8引入hooks以后,可以在函数式组件里使用state和生命周期等特性,拥有类似于类组件的能力,同时保持简洁和易用性。

在生命周期的某个阶段会触发的东西

写不写本身不会影响组件的执行,但是写的话会在生命周期的某个阶段触发

整个生命中周期分为三个阶段:初始化阶段、运行阶段、销毁阶段

初始化阶段


执行顺序:WillMount=>render=>DidMount

javascript 复制代码
import React, { Component } from 'react';

class App extends Component {
    state={
        myname:'荷叶饭'
    }
    componentWillMount() {
        console.log('WillMount', this.state.myname, document.getElementById('myname'))
        //WillUnmount,将要挂载到组件中,只会执行一次,上树(上dom树)前的最后一次修改状态的机会,但是拿不到dom
        //简单说就说初始化数据的作用
        this.setState({
            myname:'励志轩'
        })
}
    componentDidMount() {
        console.log('DidMount',document.getElementById('myname'))
        //DidMount,已经挂载到组件中,只会执行一次
        //一般放置数据请求,axios,ajax,订阅函数的调用,setInterval
        //基于创建完的dom进行初始化,BetterScroll就说基于创建完的dom来让长列表滚动

}
    render() {
        console.log('render')
        // 写完以后会自动执行
      
        return (
        //render里不能直接修改状态,所以WillMount是上树前的最后一次修改状态的机会
      <div>
            <span id='myname'>{ this.state.myname}</span>
      </div>
    );
  }
}

export default App;

React中的生命周期函数,也叫做钩子函数,钩子函数起到的作用是被咬钩的时候我知道了

这里报警告是因为WillMount 生命周期方法,该方法在 React 16.3 之后已被标记为 不安全的(unsafe),所以上面写的东西只适用于React16.2以前

我真是服了缝缝补补的前端

React版本的重要节点:16.2 之前都是老版本的生命周期函数,使用的时候可以自己将其重命名为UNSAFE_componentWillMount,相当于自己提醒自己

警告消失了:

因为在16.2以后优化了diff(新的对比旧的)算法,提出了Fiber(纤维,新协调算法,支持异步渲染、渲染优先级调度),WillMount就属于进行diff算法的部分,为第一优先级,以前来说要等它执行完毕,但是根据新的机制会打断第一优先级,所以在后期的使用就有了安全隐患
函数组件在 16.8之前也是没有生命周期的
用我们刚学的componentDidMount来做数据请求

WillMount应用实例

javascript 复制代码
import React, { Component } from 'react';
import BetterScroll from 'better-scroll'
class App extends Component {
    state = {
        list:['111','222','333','444','555','666','777','888','999','121']
    }
    UNSAFE_componentWillMount() {
        //获取不了dom节点
    }
    componentDidMount() {
        //可以获取dom节点
        new BetterScroll('#wrapper')
    }
  render() {
    return (
      <div>
            <div id='wrapper' style={{background:'yellow',height:'200px',overflow:'hidden'}}>
                <ul>
                    {
                        this.state.list.map(item => 
                            <li key={item}>{item}</li>
                        )
                    }
                </ul>
       </div>
      </div>
    );
  }
}

export default App;

运行中阶段


看看他们render、componentDidUpdate、componentWillUpdate三个的执行顺序:

javascript 复制代码
import React, { Component } from 'react';

class App extends Component {
    state = {
    myname:'kerwin'
}

    render() {
      console.log('render')
    return (
        <div>
            <button onClick={() => {
                this.setState({
                    myname:'tiechui'
                })
            }}>click</button>
       <span id='myname'> {this.state.myname}</span>
      </div>
    )
    }
    UNSAFE_componentWillUpdate() {
      console.log('componentWillUpdate',document.getElementById('myname').innerHTML)
    }
    
    componentDidUpdate() {
        console.log('componentDidUpdate',document.getElementById('myname').innerHTML)
    }
    
}

export default App;

render=>点击按钮=>componentWillUpdate=>render=>componentDidUpdate
先渲染=>点击按钮以后=>准备更新=>正在更新=>更新完成

每次更新完dom的时候new一个BetterScroll,加上if判断可以防止new多次的BetterScroll

javascript 复制代码
import React, { Component } from 'react';
import BetterScroll from 'better-scroll'

import axios from 'axios'
class App extends Component {
    state = {
        myname: 'kerwin',
        list:[]
}
    componentDidMount() {
        axios.get('/test.json').then(res => {
            console.log(res.data.data.films)
            this.setState({
                list:res.data.data.films
            },
             
            )
    })
}
    
    render() {
      console.log('render')
    return (
        <div>
            <button onClick={() => {
                this.setState({
                    myname:'tiechui'
                })
            }}>click</button>
            <span id='myname'> {this.state.myname}</span>
            
            <div id='wrapper' style={{height:'100px',overflow:'hidden',background:'pink'}}><ul>
                {
                    this.state.list.map(item => <li key={item.filmId}>{item.name}</li>)
            }
            </ul></div>
      </div>
    )
    }
    UNSAFE_componentWillUpdate() {
      console.log('componentWillUpdate',document.getElementById('myname').innerHTML)
    }
    
    componentDidUpdate(prevProps,prevState) {
        console.log('componentDidUpdate', document.getElementById('myname').innerHTML)
        if (prevState.list.length === 0) {
            new BetterScroll('#wrapper')
        }
    }
    
}

export default App;

但是如果只有这个三个普通的生命周期函数,那么只要点击按钮,即使state并没有变化,也会调用render函数

SCU

这时候就需要shouldComponentUpdate,这个函数简称SCU,性能优化函数(同理CDM是componentDidMount,),来判断组件是否应该更新:

javascript 复制代码
shouldComponentUpdate(nextProps,nextState) {
    //return true//应该更新
    //return false//阻止更新
        //做条件判断:
        if (this.state.myname!==nextState.myname) {
            return true
        }
        return false
}

如果要对比的内容是对象,可以转化为json字符串对比:
SCU优化性能的案例:

javascript 复制代码
import React, { Component } from 'react';


class Box extends Component{
//优化思路:只拿改变的div和前一个做对比就好了
    shouldComponentUpdate(nextProps) {
        if (this.props.current===this.props.index||nextProps.current===nextProps.index) {
        return true
        }
        return false
}
    render() {
        return <div
            style={{ width: '100px', height: '100px', border: this.props.current === this.props.index ? '5px solid red' : '5px solid grey', margin: '10px', float: 'left' }}>
        
        </div>
       
    }
}

class App extends Component {
    state = {
        list: ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10',],
        current:0
}
  render() {
    return (
      <div>
            <input type='number' onChange={(e) => {
                console.log(e.target.value)
                this.setState({
                    current:Number(e.target.value)
                })
            }} />
            <div style={{overflow:'hidden'}  }>
                {this.state.list.map((item, index) => <Box key={item} current={this.state.current} index={index} />)}
           </div>
      </div>
    );
  }
}

export default App;

最先获取父组件传来的属性,相当于子组件里的初始化

javascript 复制代码
import React, { Component } from 'react';

class Child extends Component{
    state = {
        title:''
    }
    render() {
        return (
            <div>
                child-{this.state.title}
            </div>
        )
    }
    UNSAFE_componentWillReceiveProps(nextProps) {
        console.log(' UNSAFE_componentWillReceiveProps',this.props.text)//第一次点击按钮拿到的是老属性,11111111
        console.log(' UNSAFE_componentWillReceiveProps', nextProps)//拿到的是新属性,22222222
        //该生命周期最先获得父组件传来的属性,可以利用属性进行ajax或者逻辑处理
        //把拿到的属性转化为自己孩子的状态
        this.setState({
            title:nextProps.text+'荷叶饭'
        })
    }
}

class App extends Component {
    state = {
    text:'11111111'
}

  render() {
    return (
        <div>
            {
                this.state.text
            }
            <button onClick={() => {
                this.setState({
                    text:'22222222'
                })
            }}>click</button>
            <Child text={this.state.text} />
      </div>
    );
  }
}

export default App;

请求到的是老的数据,需要加入形参nextProps

componentWillReceiveProps

外部组件多次频繁更新传入多次不同的 props ,会导致不必要的异步请求

javascript 复制代码
/*
 * @作者: kerwin
 * @公众号: 大前端私房菜
 */
import React, { Component } from 'react'
import axios from 'axios'
export default class App extends Component {
    state = {
        type:1
    }
    render() {
        return (
            <div>
                <ul>
                    <li onClick={()=>{
                        this.setState({
                            type:1
                        })
                    }}>正在热映</li>
                    <li onClick={()=>{
                        this.setState({
                            type:2
                        })
                    }}>即将上映</li>
                </ul>

                <FilmList type={this.state.type}></FilmList>
            </div>
        )
    }
}


class FilmList extends Component{
    state = {
        list:[]
    }

    //初始化-执行一次
    componentDidMount() {
        // console.log(this.props.type)
        if(this.props.type===1){
            //请求卖座正在热映的数据
            console.log("请求卖座正在热映的数据")
            axios({
                url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6369301",
                headers:{
                    'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529","bc":"110100"}',
                    'X-Host': 'mall.film-ticket.film.list'
                }
            }).then(res=>{
                console.log(res.data.data.films)
                this.setState({
                    list:res.data.data.films
                })
            })
        }else{
            //请求卖座即将上映的数据

            console.log("请求卖座即将上映的数据")

            axios({
                url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=2&k=8077848",
                headers:{
                    'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529","bc":"110100"}',
                    'X-Host': 'mall.film-ticket.film.list'
                }
            }).then(res=>{
                console.log(res.data.data.films)
                this.setState({
                    list:res.data.data.films
                })
            })
        }
    }
    
    UNSAFE_componentWillReceiveProps(nextProps){
        if(nextProps.type===1){
            //请求卖座正在热映的数据
            console.log("请求卖座正在热映的数据")
            axios({
                url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6369301",
                headers:{
                    'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529","bc":"110100"}',
                    'X-Host': 'mall.film-ticket.film.list'
                }
            }).then(res=>{
                console.log(res.data.data.films)
                this.setState({
                    list:res.data.data.films
                })
            })
        }else{
            //请求卖座即将上映的数据

            console.log("请求卖座即将上映的数据")
            axios({
                url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=2&k=8077848",
                headers:{
                    'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529","bc":"110100"}',
                    'X-Host': 'mall.film-ticket.film.list'
                }
            }).then(res=>{
                console.log(res.data.data.films)
                this.setState({
                    list:res.data.data.films
                })
            })
        }
    }

    render(){
        return <ul>
            {
                this.state.list.map(item=>
                <li key={item.filmId}>{item.name}</li>    
                )
            }
        </ul>
    }
}

销毁阶段

例如你在组件里写了一个定时器,即使组件删除了定时器也还在,就会降低性能,这时候就需要在销毁阶段销毁定时器

进行清理操作的生命周期函数

javascript 复制代码
import { isCancel } from 'axios';
import React, { Component } from 'react';

class App extends Component {
    state = {
    isCreated:false
}

  render() {
    return (
      <div>
            <button onClick={() => {
                this.setState({
                    isCreated:!this.state.isCreated
                })
        }}>click</button>
        {/* {this.state.isCreated?<Child></Child>:''} */}
        {this.state.isCreated&&<Child></Child>}
      </div>
    );
  }
}


class Child extends Component {
  render() {
    return (
      <div>
        child
      </div>
    )
    }
    componentDidMount() {
        window.onresize = () => {
            console.log('resize')
        }
        //绑在window上的定时器,如果只卸载组件定时器还会运行
        this.timer=setInterval(() => {
            console.log('我是定时器')
        }, 1000);
    }
    componentWillUnmount() {
        console.log('componentWillUnmount') 
        //销毁child组件的同时销毁事件监听和定时器
        clearInterval(this.timer)
        window.onresize=null
    }
}


export default App;

新的生命周期的替代

修复旧生命周期函数的警告

getDerivedStateFromProps

初始化的时候代替componentWillMount,在ssr中 这个方法将会被多次调用, 所以会重复触发多遍,同时在这里如果绑定事件, 将无法解绑,导致内存泄漏 , 变得不够安全高效逐步废弃。

在后续更新的时候取代componentWillReceiveProps 外部组件多次频繁更新传入多次不同的 props,会导致不必要的异步请求

该函数涵盖了初始化和更新阶段,适用于这两个阶段都重复执行的逻辑

第一次的初始化组件以及后续的更新过程中(包括自身状态更新以及父传子) ,

返回一个对象作为新的state,返回null则说明不需要在这里更新state

需要静态方法,返回一个对象

把传过来的state.myname转为首字母大写

javascript 复制代码
import React, { Component } from 'react';

class App extends Component {
    state = {
        myname: 'kerwin',
        myage:19
    }
    //componentWillMount 初始化
    static getDerivedStateFromProps(nextProps,nextState) {
        console.log('getDrivedStateFromProps',nextState)
        //在这个生命周期内做最后的修改,return的结果会把原来的state对象和返回的对象合并
        return {
            //实现首字母大写效果
            myname:nextState.myname.substring(0,1).toUpperCase()+nextState.myname.substring(1)
        }
}
  render() {
    return (
        <div>
            <button onClick={() => {
                this.setState({
                    myname:'heyefan'
                })
            }}>click</button>
            {/* 同名的覆盖掉,不同名的保留 */}
        app-{this.state.myname}-{this.state.myage}
      </div>
    );
  }
}

export default App;

修改之前的案例:

javascript 复制代码
import React, { Component } from 'react'
import axios from 'axios'
export default class App extends Component {
    state = {
        type:1
    }
    render() {
        return (
            <div>
                <ul>
                    <li onClick={()=>{
                        this.setState({
                            type:1
                        })
                    }}>正在热映</li>
                    <li onClick={()=>{
                        this.setState({
                            type:2
                        })
                    }}>即将上映</li>
                </ul>

                <FilmList type={this.state.type}></FilmList>
            </div>
        )
    }
}


class FilmList extends Component{
    state = {
        list:[]
    }

    //初始化-执行一次
    componentDidMount() {
        // console.log(this.props.type)
        if(this.props.type===1){
            //请求卖座正在热映的数据
            console.log("请求卖座正在热映的数据")
            axios({
                url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6369301",
                headers:{
                    'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529","bc":"110100"}',
                    'X-Host': 'mall.film-ticket.film.list'
                }
            }).then(res=>{
                console.log(res.data.data.films)
                this.setState({
                    list:res.data.data.films
                })
            })
        }else{
            //请求卖座即将上映的数据

            console.log("请求卖座即将上映的数据")

            axios({
                url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=2&k=8077848",
                headers:{
                    'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529","bc":"110100"}',
                    'X-Host': 'mall.film-ticket.film.list'
                }
            }).then(res=>{
                console.log(res.data.data.films)
                this.setState({
                    list:res.data.data.films
                })
            })
        }
    }
    
    static getDerivedStateFromProps(nextProps,nextState) {
            console.log('getDrivedStateFromProps',nextProps)
        //根据props和state的变化计算新的state,return的结果会把原来的state对象和返回的对象合并
        //此处不能执行异步请求,因为执行完会立马return,这样异步的结果就永远不会返回,也无法进行setState更新
        return {
                type:nextProps.type
            }
    }
    //通过this.setState把获取的数据转化为状态,配合componentDidUpdate
    componentDidUpdate(prevState) {
        //getDerivedStateFromProps做多次父子请求最后合并为一次,最后在此处处理
        console.log(this.state.type)
        if (this.state.type === prevState.type) {
            return
        }
        if(this.state.type===1){
            //请求卖座正在热映的数据
            console.log("请求卖座正在热映的数据")
            axios({
                url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6369301",
                headers:{
                    'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529","bc":"110100"}',
                    'X-Host': 'mall.film-ticket.film.list'
                }
            }).then(res=>{
                console.log(res.data.data.films)
                this.setState({
                    list:res.data.data.films
                })
            })
        }else{
            //请求卖座即将上映的数据

            console.log("请求卖座即将上映的数据")
            axios({
                url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=2&k=8077848",
                headers:{
                    'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529","bc":"110100"}',
                    'X-Host': 'mall.film-ticket.film.list'
                }
            }).then(res=>{
                console.log(res.data.data.films)
                this.setState({
                    list:res.data.data.films
                })
            })
        }
    }
    
    render(){
        return <ul>
            {
                this.state.list.map(item=>
                <li key={item.filmId}>{item.name}</li>    
                )
            }
        </ul>
    }
}

getSnapshotBeforeUpdate

实现定位重点功能:

willupdate记录高度

didupdate访问容器高度

scrollTop=此时容器高度-willupdate记录的高度

但是willupdate有副作用,在render的过程中容易被打断,在didupdate访问前有各种不可控事件可能发生,导致记录的高度不准

所以用getSnapshotBeforeUpdate(执行dom前的一刻拍了个shot)取代willupdate,来记录高度,所以必须有返回值


来邮件之后自动滚到之前对应邮件的高度

javascript 复制代码
import React, { Component } from 'react';

class App extends Component {
    state = {
        list: [1, 2, 3, 4, 5, 6, 7, 8, 9]
    }
    myref=React.createRef()
    getSnapshotBeforeUpdate() {
        //获取容器高度
        console.log(this.myref.current.scrollHeight)
        return this.myref.current.scrollHeight
    }
    componentDidUpdate(prevProps,prevState,value) {
        console.log(this.myref.current.scrollHeight)
        this.myref.current.scrollTop+=this.myref.current.scrollHeight-value
    }
  render() {
    return (
        <div>
            <button onClick={() => {
                this.setState({
                    list:[...[11,12,13,14,15,16],...this.state.list]
                })
            }}>来邮件</button>
            <h1>邮箱应用</h1>
            <div style={{height:'200px',overflow:'auto'}} ref={this.myref}>
                <ul>
                    {
                        this.state.list.map(item => 
                            <li key={item} style={{ height: '100px', background: 'pink' }}>{item}</li>
                        )
                    }
                </ul>
            </div>
      </div>
    )
  }
    
  
}

export default App;

可以看到添加了几个邮件可以自动跳转到对应的高度

添加css文件以后可以更丝滑

react****中性能优化的方案

1. shouldComponentUpdate

控制组件自身或者子组件是否需要更新,尤其在子组件非常多的情况下, 需要进行优化。

2. PureComponent

在引入的时候替换:

javascript 复制代码
import React, { PureComponent } from 'react';

class ComponentName extends PureComponent {
  render() {
    return (
      <div>
        Content
      </div>
    );
  }
}

export default ComponentName;

PureComponent会帮你 比较新props 跟 旧的props, 新的state和老的state(值相等,或者
对象含有相同的属性、且属性值相等 ),决定shouldcomponentUpdate 返回true 或者

false, 从而决定要不要呼叫 render function。

注意:

如果你的 state 或 props 『永远都会变』,那 PureComponent 并不会比较快,因为

shallowEqual 也需要花时间。

相关推荐
码客前端4 分钟前
理解 Flex 布局中的 flex:1 与 min-width: 0 问题
前端·css·css3
Komorebi゛4 分钟前
【CSS】圆锥渐变流光效果边框样式实现
前端·css
工藤学编程17 分钟前
零基础学AI大模型之CoT思维链和ReAct推理行动
前端·人工智能·react.js
徐同保17 分钟前
上传文件,在前端用 pdf.js 提取 上传的pdf文件中的图片
前端·javascript·pdf
怕浪猫18 分钟前
React从入门到出门第四章 组件通讯与全局状态管理
前端·javascript·react.js
博主花神19 分钟前
【React】扩展知识点
javascript·react.js·ecmascript
欧阳天风26 分钟前
用setTimeout代替setInterval
开发语言·前端·javascript
EndingCoder29 分钟前
箭头函数和 this 绑定
linux·前端·javascript·typescript
郑州光合科技余经理30 分钟前
架构解析:同城本地生活服务o2o平台海外版
大数据·开发语言·前端·人工智能·架构·php·生活
沐墨染32 分钟前
大型数据分析组件前端实践:多维度检索与实时交互设计
前端·elementui·数据挖掘·数据分析·vue·交互