生命周期
React生命周期函数汇总
react-lifecycle-methods-diagram这里可以查看各个版本的React生命周期函数。
先来看React v16.3之前的生命周期函数(图中实际上少了componentDidCatch),如下图。
React v16.4之后的生命周期函数一览图成了这样
变更
取消了
- componentWillMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
getDerivedStateFromProps(nextProps, prevState) //根据nextProps和prevState计算出预期的状态改变,返回结果会被送给setState
是一个静态函数,所以函数体内不能访问this,简单说,就是应该一个纯函数,输出完全由输入决定。每当父组件引发当前组件的渲染过程时,getDerivedStateFromProps
会被调用,这样我们有一个机会可以根据新的props和之前的state来调整新的state。
缘由
原来(React v16.0前)的生命周期在React v16推出的Fiber之后就不合适了,因为如果要开启async rendering,在render函数之前的所有函数,都有可能被执行多次。
React Fiber
本质上是为了解决 React 更新过程(更新过程是指,从调用生命周期函数开始->计算对比虚拟DOM->更新DOM树一整套流程,更新过程本身无IO操作纯CPU计算,当组件树较大时容易产生阻塞会造成页面卡顿显现影响用户体验)为同步过程的问题。ReactFiber采用时间轮片法,对更新过程进行分片,每执行完一个片区交还控制权,使更新过程本质上成为一个异步操作。具体原理点这里Lin Clark在React Conf 2017上的演讲。
对现有代码的影响
在React Fiber中,一次更新过程会分成多个分片完成,所以完全有可能一个更新任务还没有完成,就被另一个更高优先级的更新过程打断,这时候,优先级高的更新任务会优先处理完,而低优先级更新任务所做的工作则会完全作废,然后等待机会重头再来。
因为一个更新过程可能被打断,所以React Fiber一个更新过程被分为两个阶段(Phase):第一个阶段Reconciliation Phase和第二阶段Commit Phase。
在第一阶段Reconciliation Phase,React Fiber会找出需要更新哪些DOM,这个阶段是可以被打断的;但是到了第二阶段Commit Phase,那就一鼓作气把DOM更新完,绝不会被打断。
这两个阶段大部分工作都是React Fiber做,和我们相关的也就是生命周期函数。
以render函数为界,第一阶段可能会调用下面这些生命周期函数,说是“可能会调用”是因为不同生命周期调用的函数不同。
- componentWillMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
下面这些生命周期函数则会在第二阶段调用。
- componentDidMount
- componentDidUpdate
- componentWillUnmount参考
因为第一阶段的过程会被打断而且“重头再来”,就会造成意想不到的情况。componentWillMount和componentWillUpdate这两个函数往往包含副作用(例如进行一些Ajax操作),所以当使用React Fiber的时候一定要重点看这两个函数的实现。
StrictMode组件
这个组件是不渲染UI,它主要用来做检查和警告,并且这个检查只限于开发阶段,正式环境下没有什么作用。
StrictMode主要帮助
1、检查组件是否正当的使用生命周期
2、对一些使用过时的接口发出告警,比如字符串方式使用的ref
3、检查一些未预料中的会产生副作用的代码
实际使用是这么用的
1 | import React from 'react' |
Render方法新增返回类型
在React 16中,render方法支持直接返回string,number,boolean,null,portal,以及fragments(带有key属性的数组),这可以在一定程度上减少页面的DOM层级。
1 | //string |
createPortal
Portals机制提供了一种最直接的方式可以把一个子组件渲染到父组件渲染的DOM树之外。默认情况下,React组件树和DOM树是完全对应的,因此对于一些Modal,Overlay之类的组件,通常是将它们放在顶层,但逻辑上它们可能只是属于某个子组件,不利于组件的代码组织。通过使用createPortal,我们可以将组件渲染到我们想要的任意DOM节点中,但该组件依然处在React的父组件之内。带来的一个特性就是,在子组件产生的event依然可以被React父组件捕获,但在DOM结构中,它却不是你的父组件。对于组件组织,代码切割来说,这是一个很好的属性。
1 | //实现一个简易蒙层效果,抽象出一个通用的Overlay组件 |
setState传入null时不会再触发更新
比如在一个选择城市的函数中,当点击某个城市时,newValue的值可能发生改变,也可能是点击了原来的城市,值没有变化,返回null则可以直接避免触发更新,不会引起重复渲染,不需要在shouldComponentUpdate函数里面去判断。
更好的服务器端渲染
React 16的SSR被完全重写,新的实现非常快,接近3倍性能于React 15,现在提供一种流模式streaming,可以更快地把渲染的字节发送到客户端。另外,React 16在hydrating
(注:指在客户端基于服务器返回的HTML再次重新渲染)方面也表现的更好,React 16不再要求客户端的初始渲染完全和服务器返回的渲染结果一致,而是尽量重用已经存在的DOM元素。不会再有checksum(标记验证)!并对不一致发出警告。一般来说,在服务器和客户端渲染不同的内容是不建议的,但这样做在某些情况下也是有用的(比如,生成timestamp)。
lazy
1 | import React, {lazy, Suspense} from 'react'; |
内置了 react-loadable的工作
参考
https://zhuanlan.zhihu.com/p/26027085
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
https://www.jianshu.com/p/514fe21b9914
https://zhuanlan.zhihu.com/p/26027085