React的生命周期函数、DOM的diffing算法

一、生命周期的三个阶段(旧)

在这里插入图片描述

1. 初始化阶段: 由ReactDOM.render()触发—初次渲染

1.constructor()
2.componentWillMount()
3.render() —>必用,初始化渲染调用
4.componentDidMount() —>“常用”,一般做一些初始化的事情,例如:开启定时器,发送ajax请求,订阅消息。

2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发

1.shouldComponentUpdate()
2.componentWillUpdate()
3.render() —>必用,更新渲染调用
4.componentDidUpdate() —>“常用”,一般做一些收尾的事情,例如:关闭定时器,取消订阅消息。

3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发

1.componentWillUnmount()

挂载时:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>生命周期函数</title>
</head>

<body>
    <div id="test"></div>

    <script src="../js/react.development.js"></script>
    <script src="../js/react-dom.development.js"></script>
    <script src="../js/babel.min.js"></script>
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel">
        // 创建组件
        class Count extends React.Component{
            constructor(){
                super();
                this.state = {count:0};
                console.log('constructor');
            }
            
            add = () => {
                let {count} = this.state;
                this.setState({
                    count:count+1
                })
            }

            death = () => {
                ReactDOM.unmountComponentAtNode(document.getElementById('test'));
            }

            force = () =>{
                this.forceUpdate();
            }

            // 组件将要挂载的钩子
            componentWillMount(){
                console.log('componentWillMount');
            }

            // 组件挂载完毕的钩子
            componentDidMount(){
                console.log('componentDidMount');
            }

            // 组件将要卸载的钩子
            componentWillUnmount(){
                console.log('componentWillUnmount');
            }

            // 控制组件更新的阀门
            shouldComponentUpdate(){
                console.log('shouldComponentUpdate');
                return false;
            }

            // 组件将要更新钩子
            componentWillUpdate(){
                console.log('componentWillUpdate');
            }
            // 组件更新完毕的钩子
            componentDidUpdate(){
                console.log('componentDidUpdate');
            }

            render(){
                console.log('render');
                let {count} = this.state;
                return (
                    <div>
                        <h2>当前求和为{count}</h2>  
                        <button onClick={this.add}>点我+1</button> 
                        <button onClick={this.death}>卸载组件</button> 
                        <button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
                    </div>
                )
            }
        }

        // 渲染组件
        ReactDOM.render(<Count/>,document.getElementById('test'));
    </script>
</body>

</html>

父组件render:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>生命周期函数</title>
</head>

<body>
    <div id="test"></div>

    <script src="../js/react.development.js"></script>
    <script src="../js/react-dom.development.js"></script>
    <script src="../js/babel.min.js"></script>
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel">
        // 创建组件  
        class A extends React.Component{
            state = {carName:'奔驰'};
            changeCar = () =>[
                this.setState({
                    carName:'宝马'
                })
            ]
            render(){
                return(
                    <div>
                        <div>我是A组件</div>
                        <button onClick={this.changeCar}>换车</button>
                        <B carName={this.state.carName}/>
                    </div>
                )
            }
        }

        class B extends React.Component{            
            // 组件将要接收新的props钩子
            componentWillReceiveProps(props){
                console.log('B----componentWillReceiveProps',props);
            }

            // 控制组件更新的阀门
            shouldComponentUpdate(){
                console.log('shouldComponentUpdate');
                return true;
            }

            // 组件将要更新钩子
            componentWillUpdate(){
                console.log('componentWillUpdate');
            }

            // 组件更新完毕的钩子
            componentDidUpdate(){
                console.log('componentDidUpdate');
            }

            render(){
                console.log('B---render');
                return(
                    <div>我是B组件,接收到的车是:{this.props.carName}</div>
                )
            }
        }
        
        // 渲染组件
        ReactDOM.render(<A/>,document.getElementById('test'));
    </script>
</body>

</html>

二、生命周期流程图(新)

在这里插入图片描述

1. 初始化阶段: 由ReactDOM.render()触发—初次渲染

1.constructor()
2.getDerivedStateFromProps
3.render() —>必用
4.componentDidMount() —>“常用”,一般做一些初始化的事情,例如:开启定时器,发送ajax请求,订阅消息。

2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发

1.getDerivedStateFromProps
2.shouldComponentUpdate()
3.render() —>必用
4.getSnapshotBeforeUpdate
4.componentDidUpdate() —>“常用”,一般做一些收尾的事情,例如:关闭定时器,取消订阅消息。

3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发

1.componentWillUnmount()

新生命周期函数演示:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>生命周期函数</title>
</head>

<body>
    <div id="test"></div>

    <script src="../js/17.0.1/react.development.js"></script>
    <script src="../js/17.0.1/react-dom.development.js"></script>
    <script src="../js/17.0.1/babel.min.js"></script>
    <script type="text/javascript" src="../js/17.0.1/prop-types.js"></script>

    <script type="text/babel">
        // 创建组件
        class Count extends React.Component{
            constructor(){
                super();
                this.state = {count:0};
                console.log('constructor');
            }
            
            add = () => {
                let {count} = this.state;
                this.setState({
                    count:count+1
                })
            }

            death = () => {
                ReactDOM.unmountComponentAtNode(document.getElementById('test'));
            }

            force = () =>{
                // this.forceUpdate();
            }

            // 若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
            static getDerivedStateFromProps(props,state){
                console.log('getDeriveStateFromProps',props,state);
                // return props;
                return null;
            }

            // 在更新之前获取快照
            getSnapshotBeforeUpdate(){
                console.log('getSnapshotBeforeUpdate');
                return 1111; // 1111-->传给了componentDidUpdate的第三个参数
            }

            // 组件挂载完毕的钩子
            componentDidMount(){
                console.log('componentDidMount');
            }

            // 组件将要卸载的钩子
            componentWillUnmount(){
                console.log('componentWillUnmount');
            }

            // 控制组件更新的阀门
            shouldComponentUpdate(){
                console.log('shouldComponentUpdate');
                return true;
            }


            // 组件更新完毕的钩子
            componentDidUpdate(preProps,preState,snapshotvalue){
                console.log('componentDidUpdate',preProps,preState,snapshotvalue);
            }

            render(){
                console.log('render');
                let {count} = this.state;
                return (
                    <div>
                        <h2>当前求和为{count}</h2>  
                        <button onClick={this.add}>点我+1</button> 
                        <button onClick={this.death}>卸载组件</button> 
                        <button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
                    </div>
                )
            }
        }

        // 渲染组件
        ReactDOM.render(<Count count={199}/>,document.getElementById('test'));
    </script>
</body>

</html>

getSnapshotBeforeUpdate案例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>生命周期函数</title>
    <style>
        .list {
            width: 200px;
            height: 150px;
            background-color: skyblue;
            overflow: auto;
        }

        .news {
            height: 30px;
        }
    </style>
</head>

<body>
    <div id="test"></div>

    <script src="../js/17.0.1/react.development.js"></script>
    <script src="../js/17.0.1/react-dom.development.js"></script>
    <script src="../js/17.0.1/babel.min.js"></script>
    <script type="text/javascript" src="../js/17.0.1/prop-types.js"></script>

    <script type="text/babel">
        // 创建组件
        class NewList extends React.Component{
            state = {newsArr:[]};
            componentDidMount(){
                setInterval(()=>{
                    const {newsArr} = this.state;
                    const news = '新闻'+(newsArr.length+1);
                    this.setState({
                        newsArr:[news,...newsArr]
                    })
                },1000)
            }

            getSnapshotBeforeUpdate(){
                return this.refs.list.scrollHeight;
            }

            componentDidUpdate(preProps,preState,height){
                this.refs.list.scrollTop += this.refs.list.scrollHeight - height;
            }
            render(){
                return(
                    <div className="list" ref="list">
                        {this.state.newsArr.map((item,index)=>{
                            return <div className="news" key={index}>{item}</div>
                        })}
                    </div>
                )
            }
        }
        // 渲染组件
        ReactDOM.render(<NewList/>,document.getElementById('test'));
    </script>
</body>

</html>

【面试提问】react新旧生命周期的对比?

react新生命周期即将废弃的勾子:

  • 1.componentWillMount
  • 2.componentWillReceiveProps
  • 3.componentWillUpdate
    原因:现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。

react新生命周期新增加的钩子:

  • getDerivedStateFromProps
  • getSnapshotBeforeUpdate

三、DOM的diffing算法

基本原理图:
在这里插入图片描述

经典面试题:

  • 1). react/vue中的key有什么作用?(key的内部原理是什么?)
  • 2). 为什么遍历列表时,key最好不要用index?

原因如下:

1. 虚拟DOM中key的作用:
	1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。

	2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:

		a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
			(1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
			(2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM

		b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
			根据数据创建新的真实DOM,随后渲染到到页面
			
2. 用index作为key可能会引发的问题:
	1). 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
		会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

	2). 如果结构中还包含输入类的DOM:
		会产生错误DOM更新 ==> 界面有问题。
												
	3). 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
		仅用于渲染列表用于展示,使用index作为key是没有问题的。

3. 开发中如何选择key?:
	1). 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
	2). 如果确定只是简单的展示数据,用index也是可以的。

代码演示:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>生命周期函数</title>
    <style>
        .list {
            width: 200px;
            height: 150px;
            background-color: skyblue;
            overflow: auto;
        }

        .news {
            height: 30px;
        }
    </style>
</head>

<body>
    <div id="test"></div>

    <script src="../js/17.0.1/react.development.js"></script>
    <script src="../js/17.0.1/react-dom.development.js"></script>
    <script src="../js/17.0.1/babel.min.js"></script>
    <script type="text/javascript" src="../js/17.0.1/prop-types.js"></script>

    <script type="text/babel">
        /* 
		慢动作回放----使用index索引值作为key

			初始数据:
					{id:1,name:'小张',age:18},
					{id:2,name:'小李',age:19},
			初始的虚拟DOM:
					<li key=0>小张---18<input type="text"/></li>
					<li key=1>小李---19<input type="text"/></li>

			更新后的数据:
					{id:3,name:'小王',age:20},
					{id:1,name:'小张',age:18},
					{id:2,name:'小李',age:19},
			更新数据后的虚拟DOM:
					<li key=0>小王---20<input type="text"/></li>
					<li key=1>小张---18<input type="text"/></li>
					<li key=2>小李---19<input type="text"/></li>

	-----------------------------------------------------------------

		慢动作回放----使用id唯一标识作为key

			初始数据:
					{id:1,name:'小张',age:18},
					{id:2,name:'小李',age:19},
			初始的虚拟DOM:
					<li key=1>小张---18<input type="text"/></li>
					<li key=2>小李---19<input type="text"/></li>

			更新后的数据:
					{id:3,name:'小王',age:20},
					{id:1,name:'小张',age:18},
					{id:2,name:'小李',age:19},
			更新数据后的虚拟DOM:
					<li key=3>小王---20<input type="text"/></li>
					<li key=1>小张---18<input type="text"/></li>
					<li key=2>小李---19<input type="text"/></li>
	 */
	
        // 创建组件
        class Person extends React.Component{
            state = {
                persons:[
                    {id:1,name:'小张',age:18},
                    {id:2,name:'小李',age:19},
                ]
            }
            add=()=>{
                const {persons} = this.state;
                const p = {id:persons.length+1,name:'小王',age:20}
                this.setState({
                    persons:[p,...persons]
                })
            }
            render(){   
                console.log(this.state.persons);       
                return(
                    <div>
                        <h1>展示人员信息</h1>
                        <button onClick={this.add}>添加一个小王</button>
                        <ul>
                            {this.state.persons.map((item)=>{
                                return <li key={item.id}>{item.name}---{item.age}</li>
                            })}    
                        </ul>    
                    </div>
                )
            }
        }
        // 渲染组件
        ReactDOM.render(<Person/>,document.getElementById('test'));
    </script>
</body>

</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值