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 也需要花时间。

相关推荐
代码CC5 分钟前
Vue.js+Element UI 登录界面开发详解【附源码】
前端·vue.js·ui·elementui
无名之逆7 分钟前
Hyperlane:Rust 语言打造的 Web 后端框架新标杆
开发语言·前端·网络·网络协议·rust·github·ssl
冰夏之夜影12 分钟前
【css酷炫效果】纯CSS实现悬浮弹性按钮
前端·css
shadouqi20 分钟前
4.angular 服务
前端·javascript·angular.js
努力往上爬de蜗牛20 分钟前
react学习1.搭建react环境
javascript·学习·react.js
JaxNext21 分钟前
成为谷歌开发者专家,也成为儿时心中的侠客
前端·程序员·开源
一个处女座的程序猿O(∩_∩)O32 分钟前
Webpack vs Rollup vs Parcel:构建工具深度对比
前端·webpack·devops
小鱼冻干1 小时前
express中间件
前端·mysql·node.js
范哥来了1 小时前
python web开发flask库安装与使用
前端·python·flask
顾林海1 小时前
Flutter Dart 泛型详解
android·前端·flutter