【React】03-React面向组件编程2

文章目录

    • [2.6. 组件的生命周期](#2.6. 组件的生命周期)
      • [2.6.1. 效果](#2.6.1. 效果)
      • [2.6.2. 理解](#2.6.2. 理解)
      • [2.6.3. 生命周期流程图(旧)](#2.6.3. 生命周期流程图(旧))
      • [2.6.4. 生命周期流程图(新)](#2.6.4. 生命周期流程图(新))
      • [2.6.5. ==重要的勾子==](#2.6.5. ==重要的勾子==)
      • [2.6.6. 即将废弃的勾子](#2.6.6. 即将废弃的勾子)
      • [2.6.7 getSnapshotBeforeUpdate](#2.6.7 getSnapshotBeforeUpdate)
    • [2.7. 虚拟DOM与DOM Diffing算法](#2.7. 虚拟DOM与DOM Diffing算法)
      • [2.7.1. 效果](#2.7.1. 效果)
      • [2.7.2. 基本原理图](#2.7.2. 基本原理图)

2.6. 组件的生命周期

2.6.1. 效果

需求:定义组件实现以下功能:

  1. 让指定的文本做显示 / 隐藏的渐变动画
  2. 从完全可见,到彻底消失,耗时2S
  3. 点击"不活了"按钮从界面中卸载组件
react 复制代码
<!-- 准备好一个"容器" -->
<div id="test"></div>
<!-- 引入三个库 -->
<script type="text/babel">
  
    class Demo extends React.Component{
       
        state = {opacity:1}

        death = ()=>{
            //卸载组件
            ReactDOM.unmountComponentAtNode(document.getElementById('test'))
        }

        //组件挂完毕
        componentDidMount(){
            console.log('componentDidMount');
            this.timer = setInterval(() => {
                //获取原状态
                let {opacity} = this.state
                //减小0.1
                opacity -= 0.1
                if(opacity <= 0) opacity = 1
                //设置新的透明度
                this.setState({opacity})
            }, 200);
        }

        //组件将要卸载
        componentWillUnmount(){
            //清除定时器
            clearInterval(this.timer)
        }

        //初始化渲染、状态更新之后
        render(){
            console.log('render');
            return(
                <div>
                    <h2 style={{opacity:this.state.opacity}}>React学不会怎么办?</h2>
                    <button onClick={this.death}>不活了</button>
                </div>
            )
        }
    }
		
    //渲染组件到页面
	ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
</script>

2.6.2. 理解

1.组件从创建到死亡它会经历一些特定的阶段。

2.React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。

3.我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。

2.6.3. 生命周期流程图(旧)

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

  1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
    1.constructor()
    2.componentWillMount()
    3.render()
    4.componentDidMount()
  2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
    1.shouldComponentUpdate()=
    2.componentWillUpdate()
    3.render()
    4.componentDidUpdate()
  3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
    1.componentWillUnmount()
react 复制代码
<!-- 准备好一个"容器" -->
<div id="test"></div>
<!-- 引入三个库 -->
<script type="text/babel">
    /* 
        1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
                            1.	constructor()
                            2.	componentWillMount()
                            3.	render()
                            4.	componentDidMount() =====> 常用
                                            一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
        2. 更新阶段: 由组件内部this.setSate()或父组件render触发
                            1.	shouldComponentUpdate()
                            2.	componentWillUpdate()
                            3.	render() =====> 必须使用的一个
                            4.	componentDidUpdate()
        3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
                            1.	componentWillUnmount()  =====> 常用
                                            一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
		*/
    class Demo extends React.Component{
        constructor(props){
            console.log("Count---construtor");
            super(props)
            //初始化状态
            this.state = {count:0}
        }
        //加按钮的回调
        add = () =>{
            //获取原状态
            const {count} = this.state;
            this.setState({count:count+1})
        }
        //卸载组件按钮的回调
        dath = ()=>{
            React.unmountComponentAtNode(document.getElementById('test'))
        }
        
        //强制更新的按钮
        force = ()=>{
            this.forceUpdate()
        }
        
        //组件将要挂在的钩子函数
        componentWillMount(){
            console.log('Count---componentWillMount');
        }
        
        //组件挂在完毕
        componentDidMount(){
            console.log('Count---componentDidMount');
        }
        
        //组件将要卸载的钩子
        componentWillUnmount(){
            console.log('Count---componentWillUnmount');
        }
        
        //控制组件更新的阀门,默认为true,必须要有返回值
        shouldComponentUpdate(){
            console.log('Count---shouldComponentUpdate');
            return true
        }
        
        //组件将要更新的钩子
        componentWillUpdate(){
            console.log('Count---componentWillUpdate');
        }
        
        //组件更新完毕的钩子
        componentDidUpdate(){
            console.log('Count---componentDidUpdate');
        }
        
        render(){
            console.log('Count---render');
            const {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>
            )
        }     
        
    }
    //父组件A
    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>
            )
        }
    }

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

        //控制组件更新的"阀门"
        shouldComponentUpdate(){
            console.log('B---shouldComponentUpdate');
            return true
        }
        //组件将要更新的钩子
        componentWillUpdate(){
            console.log('B---componentWillUpdate');
        }

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

        render(){
            console.log('B---render');
            return(
                <div>我是B组件,接收到的车是:{this.props.carName}</div>
            )
        }
    }
		
    //渲染组件到页面
	ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
</script>

2.6.4. 生命周期流程图(新)

生命周期的三个阶段(新)

  1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
    1.constructor()
    2.getDerivedStateFromProps
    3.render()
    4.componentDidMount()
  2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
    1.getDerivedStateFromProps
    2.shouldComponentUpdate()
    3.render()
    4.getSnapshotBeforeUpdate
    5.componentDidUpdate()
  3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
    1.componentWillUnmount()
react 复制代码
<!-- 准备好一个"容器" -->
<div id="test"></div>
<!-- 引入三个库 -->
<script type="text/babel">
    /* 
       1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
                        1.	constructor()
                        2.	getDerivedStateFromProps 
                        3.	render()
                        4.	componentDidMount() =====> 常用
                                    一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
        2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
                        1.	getDerivedStateFromProps
                        2.	shouldComponentUpdate()
                        3.	render()
                        4.	getSnapshotBeforeUpdate
                        5.	componentDidUpdate()
        3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
                        1.	componentWillUnmount()  =====> 常用
                                    一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
		*/
    class Demo extends React.Component{
       //构造器
        constructor(props){
            console.log('Count---constructor');
            super(props)
            //初始化状态
            this.state = {count:0}
        }

        //加1按钮的回调
        add = ()=>{
            //获取原状态
            const {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('getDerivedStateFromProps',props,state);
            return null
        }

        //在更新之前获取快照
        getSnapshotBeforeUpdate(){
            console.log('getSnapshotBeforeUpdate');
            return 'atguigu'
        }

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

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

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

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

        render(){
            console.log('Count---render');
            const {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(<Demo a="1" b="2"/>,document.getElementById('test'))
</script>

2.6.5. 重要的勾子

1.render:初始化渲染或更新渲染调用

2.componentDidMount:开启监听, 发送ajax请求

3.componentWillUnmount:做一些收尾工作, 如: 清理定时器

2.6.6. 即将废弃的勾子

1.componentWillMount

2.componentWillReceiveProps

3.componentWillUpdate

现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。

2.6.7 getSnapshotBeforeUpdate

react 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>4_getSnapShotBeforeUpdate的使用场景</title>
	<style>
		.list{
			width: 200px;
			height: 150px;
			background-color: skyblue;
			overflow: auto;
		}
		.news{
			height: 30px;
		}
	</style>
</head>
<body>
	<!-- 准备好一个"容器" -->
	<div id="test"></div>
	
	<!-- 引入react核心库 -->
	<script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
	<!-- 引入react-dom,用于支持react操作DOM -->
	<script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
	<!-- 引入babel,用于将jsx转为js -->
	<script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

	<script type="text/babel">
		class NewsList 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((n,index)=>{
								return <div key={index} className="news">{n}</div>
							})
						}
					</div>
				)
			}
		}
		ReactDOM.render(<NewsList/>,document.getElementById('test'))
	</script>
</body>
</html>

2.7. 虚拟DOM与DOM Diffing算法

2.7.1. 效果

需求:验证虚拟DOM Diffing算法的存在

2.7.2. 基本原理图

html 复制代码
经典面试题:
      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也是可以的。
相关推荐
诗书画唱1 分钟前
【前端面试题】JavaScript 核心知识点解析(第二十二题到第六十一题)
开发语言·前端·javascript
excel8 分钟前
前端必备:从能力检测到 UA-CH,浏览器客户端检测的完整指南
前端
前端小巷子14 分钟前
Vue 3全面提速剖析
前端·vue.js·面试
悟空聊架构21 分钟前
我的网站被攻击了,被干掉了 120G 流量,还在持续攻击中...
java·前端·架构
CodeSheep22 分钟前
国内 IT 公司时薪排行榜。
前端·后端·程序员
尖椒土豆sss26 分钟前
踩坑vue项目中使用 iframe 嵌套子系统无法登录,不报错问题!
前端·vue.js
遗悲风27 分钟前
html二次作业
前端·html
江城开朗的豌豆31 分钟前
React输入框优化:如何精准获取用户输入完成后的最终值?
前端·javascript·全栈
CF14年老兵31 分钟前
从卡顿到飞驰:我是如何用WebAssembly引爆React性能的
前端·react.js·trae
画月的亮34 分钟前
前端处理导出PDF。Vue导出pdf
前端·vue.js·pdf