Javascript

初级前端-React第一步:生命周期总结

本文主要是介绍初级前端-React第一步:生命周期总结,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

整体预览

挂载阶段

render

class 组件中唯一必须实现的方法

当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:

  • React 元素
  • 数组或 fragments
  • Portals
  • 字符串或数值类型
  • 布尔类型或 null

render函数应该为纯函数(可以使组件更容易思考) 这意味着在不修改组件state的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。

Render函数只用来输出页面,不存在副作用(包括浏览器交互行为)

注意

如果 shouldComponentUpdate() 返回 false,则不会调用 render()。

constructor

初始化 state 或进行方法绑定( this ), 非必要生命周期函数;

在 React 组件挂载之前,会调用它的构造函数。在为React.Component子类时,实现构造函数应在其他语句之前调用 super(props)。

通常,在 React 中,构造函数仅用于以下两种情况:

在 constructor() 函数中不要调用 setState() 方法。 @todo: 在constructor中调用setState会发生什么?

不能调用setState的原因:state还在初始化,获取不到state,因此会报错吗?

componentDidMount

当组件挂载真实DOM中后,会执行该生命周期钩子函数。

常见使用场景

  • 添加副作用
  • 可以使用DOM

static getDerivedStateFromProps

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。 它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。

不管原因是什么,都会在每次渲染前触发此方法(16.3版本只存在挂载时),与 UNSAFE_componentWillReceiveProps 形成对比,后者仅在父组件重新渲染时触发,而不是在内部调用 setState 时。

避免了开发者在使用过程中还需要特殊记忆何时能够使用该静态方法,点赞!

更新阶段

shouldComponentUpdate

根据 shouldComponentUpdate() 的返回值,判断 React 组件的显示是否受当前 state 或 props 更改的影响。

在首次渲染或使用forceUpdate时不会调用该方法,当 props 或 state 发生变化时,shouldComponentUpdate 会在渲染执行之前被调用。

此方法仅作为 性能优化的方式 而存在,不要企图依靠此方法来“阻止”渲染。

你应该考虑使用内置的 PureComponent 组件,而不是手动编写 shouldComponentUpdate()。

PureComponent 会自动对props和state进行浅层比较,并减少了跳过必要更新的可能性

手动编写此函数

可以将 this.props 与 nextProps以及 this.state 与nextState 进行比较,并返回 false 以告知 React 可以跳过更新。请注意,返回 false 并不会阻止子组件在 state 更改时重新渲染。

不建议在 shouldComponentUpdate() 中进行深层比较或使用 JSON.stringify() 这样非常影响效率,且会损害性能。

目前版本中,scu 返回 false后,会直接终止以下的生命周期执行(…WillUpadate, render, …DIdUpdate) 在后续版本中,scu 很有可能会变成提示,而不是严格的指令(能够执行的函数),并且在返回false时,很有可能仍然会导致组件的渲染

getSnapshotBeforeUpdate

在最近一次渲染输出(提交到 DOM 节点)之前调用,它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate

除了官方提供的例子外,想不到其他应用场景

componentDidUpdate

会在更新后会被立即调用。首次渲染不会执行此方法

可以在 componentDidUpdate() 中直接调用setState(),但请注意它必须被包裹在一个条件语句里,否则会导致死循环。

卸载阶段

componentWillUnmount

会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作

  • 清除 timer
  • 取消网络请求
  • 清除在 componentDidMount() 中创建的事件订阅

componentWillUnmount 中不应调用setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。

将永远不会再挂载它,说明该实例已经在内存中销毁掉?

React Fiber架构对生命周期的影响

在版本更替过程中,deprecate的生命周期函数

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

15版本的生命周期

其根本性原因是React在16版本中引入了FIber架构:React Fiber是对核心算法的一次重新实现 由于React Fiber需要开启 async rendering,在render函数之前的所有函数,都有可能被执行多次

在原有的生命周期函数总是会诱惑开发者在render之前的生命周期函数做一些动作,没有Fiber时,不痛不痒,不会多次执行。有了Fiber后,React团队为了避免不必要的麻烦(多次执行某些生命周期函数),决定精简生命周期,不再追求对称的美学,来点实际的,让程序员断了在这些生命周期函数里做些不该做事情的念想。

发送AJAX不在componentWillMount中的原因:可以从Fiber的角度来讲,何况hooks出现了…

替代方案:getDerivedStateFromProps

按照官方说法,以前需要利用被deprecate的所有生命周期函数才能实现的功能,都可以通过getDerivedStateFromProps的帮助来实现。

静态函数:函数体内不能访问this,简单说,就是应该一个纯函数

static getDerivedStateFromProps(nextProps, prevState) {
  //根据nextProps和prevState计算出预期的状态改变,返回结果会被送给setState
}
复制代码

体会React的潜台词:老实做一个运算就行,别在这里搞什么别的动作

所有被deprecate的生命周期函数,目前还凑合着用,但是只要用了,开发模式下会有红色警告,在下一个大版本(也就是React v17)更新时会彻底废弃。

说getDerivedStateFromProps取代componentWillReceiveProps是不准确的,因为componentWillReceiveProps只在Updating过程中才被调用,而且只在因为父组件引发的Updating过程中才被调用(往上翻看图);而getDerivedStateFromProps在Updating和Mounting过程中都会被调用(无论是Mounting还是Updating,也无论是因为什么引起的Updating,全部都会被调)。

可能是state、props的改变,或forceUpdate

用一个静态函数getDerivedStateFromProps来取代被deprecate的几个生命周期函数,就是强制开发者在render之前只做无副作用的操作,而且能做的操作局限在根据props和state决定新的state而已。

施加约束的哲学指导思想

理解一下FIber产生的原因:

同步更新 + 单线程 = 阻塞

在16版本以前,React框架在渲染组件时会经历如下过程:

  • 调用组件的生命周期函数
  • 计算和比对Virtual DOM
  • 最后更新DOM树

整个过程是同步进行的,也就是说只要一个加载或者更新过程开始,那React就以不破楼兰终不还的气概,一鼓作气运行到底,中途绝不停歇。

表面上看,合理,更新过程不会有任何I/O操作,完全是CPU计算,所以无需异步操作。但是,当组件树比较庞大的时候,容易出现界面卡顿(GUI渲染输出线程被JavaScript线程抢占 ) 拉低用户体验。

@todo: 浏览器架构

函数的调用栈示例图:

因为JavaScript单线程的特点,会阻塞其他线程的执行,所以每个同步任务需要执行的速度必须很快,否则就会造成页面卡顿。

React更新过程是同步更新,因此导致性能问题产生,React Fiber力图改变此现状。

解决方案 React Fiber

把一个耗时长的任务分成很多小片,虽然总时间依然很长,但胜在每一个小片的运行时间很短,有机会将任务中途交给另外一个权重高的任务执行。

React Fiber把更新过程碎片化,每执行完一段更新过程,就把控制权交还给React负责任务协调的模块,看看有没有其他紧急任务要做,如果没有就继续去更新,如果有紧急任务,那就去做紧急任务。 分片的数据结构,就是Fiber。

波峰时提供紧急任务调度能力

在计算机领域中,存在进程(Process)和线程(Thread)的概念,另外还有一个概念叫做Fiber,英文含义就是“纤维”,意指比线程(Thread)控制得更精密的并发处理机制

FIber对已有代码的影响

在React Fiber中,一次更新过程会分成多个分片完成,所以完全有可能一个更新任务还没有完成,就被另一个更高优先级的更新过程打断。这时候,优先级高的更新任务会优先处理完,而低优先级更新任务所做的工作则会完全作废,然后等待机会重头再来

React Fiber一个更新过程被分为两个阶段(Phase):第一个阶段Reconciliation Phase和第二阶段Commit Phase。

第一阶段Reconciliation Phase,React Fiber会找出需要更新哪些DOM,这个阶段是可以被打断的;但是到了第二阶段Commit Phase,那就一鼓作气把DOM更新完,绝不会被打断。

在16版本前每个生命周期函数在一个加载或者更新过程中绝对只会被调用一次;在React Fiber中,不再是这样了,第一阶段中的生命周期函数在一次加载和更新过程中可能会被多次调用!

因此使用React Fiber之后,一定要检查一下第一阶段相关的这些生命周期函数,看看有没有逻辑是假设在一个更新过程中只调用一次,有的话就要改。

通常在componentWillMount和componentWillUpdate这两个函数往往包含副作用,所以当使用React Fiber的时候一定要重点看这两个函数的实现。

关于Hooks部分的生命周期,归入到Hooks部分笔记中,届时会和类组件生命进行对比回顾。对于之后的 深入理解 生命周期,会以生命周期续形式补充,并链接。

这篇关于初级前端-React第一步:生命周期总结的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!