本篇博文基于 React 16.5.2javascript
做为一个后端开发,15年开始关注大前端发展趋势,于17年去线下听了场前端开发会议,那个时候Vue2.0刚出没多久,就被那快速构建页面给吸引了。最先重返前端仍是大半年前,新项目用vue写了几个功能页面,发现如今写前端是真挺舒服,尤为是对于后端人员来讲(排除掉CSS),快速入门并上手不是什么问题。html
至于为何最终选择了react而非vue?是由于当时对react和vue及RN和weex作了番调研对比,介于weex的不给力,及后期react和vue学习成本差很少,但react的社区更为活跃,外加发起者背景,就毅然选择了react。(我我的是通一精百的支持者,因此对于react的理念(learn once,write anywhere),是很同意的。而weex的理念(write once,run anywhere)虽然很吸引人,但时下我的以为技术并未达到此程度,配置的复杂度及大量的轮子需造,难以知足大型项目的要求。包括目前JD推出的Taro,我的目前持观望态度,等到react这块应用到项目以后,再码一波Taro实际调研一番)。前端
接触React的时候已是React 16.3,不由感慨前端发展至今,越有后端的趋势。先后花了3个多月的时间过了一遍webpack4,npm,react,在公司内部作了几场培训,发现了其中的一些不协调,但随着版本的迭代,这些不协调也依次在被更正。(看React17的更新内容,本来一些摸棱两可的方法、属性元素命名均会获得改善:)vue
后续会以此文为契机,开一个专栏,记录、分享公司现有电商项目逐步迁移至react技术栈。java
本篇博文会对React16.5.2中的经常使用生命周期函数作一些翻译、讲解说明及发表一些我的的理解和见解,若有错误、歧义之处,还望你们不吝啬指出,互相交流:)react
开篇借用官方提供的生命周期函数图:(虽然是React 16.4的,但一样适用于16.5版本) webpack
react组件生命周期函数说明官方地址:reactjs.org/docs/react-…web
getDerivedStateFromProps()方法的16.3版本与以后的16.4和16.5有所调整:npm
在16.3版本上,该方法仅在props变更时才会被触发。canvas
在16.3以后的版本上,该方法调整为props和state的变更都会触发该方法。
从上图中,咱们能够看到,React的生命周期函数主要集中在三个阶段:挂载阶段(Mounting),更新阶段(Updating)和卸载阶段(Unmounting)。
其中:
下面咱们就对以上生命周期函数作进一步说明:
class TestContainer extends PureComponent {
state = {
content:"1" //先执行
}
constructor(props){
super(props);
this.state={
content:"2" //后执行,最终 this.state.content 为 2
}
}
。。。
}
复制代码
这里的 side-effects 方法,翻译出来是 "反作用" 的意思,我的以为不妥,有点生涩,可能翻译成"附加做用"更为稳当。
它指的是,render()方法应该只完成它的主要功能(如初始化state和绑定方法),不该顺带完成其余的附加功能,如统计,动画等。
该方法的应用场景:根据特定的props变换,来触发state的更新。如咱们可能会有一个canvas用来表示页面loading百分比,这个时候就能够根据传入的nextProps中的百分比属性跟当前state的百分比属性作对比,若不一致,则更新,以下:
componentWillReceiveProps(nextProps) {
if (this.props.percent !== nextProps.percent) {
this.setUpCircle(nextProps.percent);
}
}
复制代码
该方法的应用场景:使组件可以根据父组件传入的 props 来更新其自身的 state
在16.4版本以前,只有props更新才会触发该方法;但FB在16.4版本作了完善,目前 props 和 state 的更新均会触发该方法。
使用场景:能够在根组件的componentWillMount中作一些App的配置。
组件中惟一必须的方法。
render()方法的返回类型目前有 string,number,boolean,null,portal 以及 fragments(带有key属性的数组),eg:
render() {
return "string"; //string
return 123; //number
return true; //若是返回的是false,则什么都不渲染
return null; //若是返回的是null,则什么都不渲染
return ReactDOM.createPortal(<MyComponent></MyComponent>, domNode);//portal
return [
<div key="unique_1">若是返回的是数组类型</div>,
<span key="unique_2">须要在每一个html元素上加上 key 值</span>, //fragments
<p key="unique_3">不然控制台会报错</p>
]
}
复制代码
须要注意的是,组件在更新的时候,也会触发 render() 方法,但若是 shouldComponentUpdate() 返回的是false,则在"更新阶段",render()方法不会被触发。
该方法应为一个纯函数,除了作渲染的动做外,不该顺带完成其余动做(如 setState 等),即应避免 side-effects。
应尽可能仅在 render() 这个生命周期函数中中从 this.props 和 this.state 中读取数据。
使用场景:总体来讲就是作“获取一些数据”,或者作必需要有 DOM 才能作的设置。
排除了 UNSAFE 的方法。
使用场景:通常用于精确控制组件是否须要从新渲染(这个方法通常用于性能优化),在绝大多数状况下,每次 state 的更改都应该让组件从新渲染。
- 只要有一个字段进行了更新,那么其余全部字段都会进行更新,这个会减慢页面速度。(经过shouldComponentUpdate,容许咱们只有当咱们关心的 props 更新的时候,才进行组件的更新)。
- 但须要谨慎使用,由于一旦你本身忘记实现了这个方法,可能会致使你的react组件没法正常更新。
使用场景:当你实现了 shouldComponentUpdate (返回 true时) 而且在 props 更改时须要执行某些操做的时候,那么 componentWillUpdate 这个方法仍是有点意义的,但我的认为,做用不是太大,反却是增长了理解的复杂度,被删除也是情理之中。
使用场景:
- 通常在这个方法中进行 RPC 请求。(能够比较下先前的 props 和当前的 props 是否一致,若一致,则能够不用请求网络数据。)
- 若是想要在DOM自身更新以后作一些动做(如从新排列网格等),那么能够在这个方法中进行。
排除了 UNSAFE 的方法。
这里须要注意下,该函数是 will unmount,因此触发顺序上是从父组件到子组件,但释放顺序上,是最内层的子组件先释放,最终最外层的根组件才释放。
使用场景:UI中的一些JS错误不该该使整个App崩溃,为了解决这个问题,React16中引入了Error Boundary(错误边界)这个概念,旨在解决容许页面的部分组件异常但不影响App的渲染。能够认为是组件中的 try-catch。而为了实现这个功能,就须要借助 componentDidCatch() 这个生命周期函数。
只要该组件内部实现了 componentDidCatch 这个方法就能够认为是 ErrorBoundary 组件,如:
...
class MyErrorBoundary extends Component{
...
componentDidCatch(error,info){
...
}
...
}
复制代码
MyErrorBoundary:
import React, { Component } from 'react';
class MyErrorBoundary extends Component {
state = {
isError: false
}
render() {
if (this.state.isError) {
return (<div>sth wrong here.</div>);
}
return this.props.children;
}
componentDidCatch(error, info) {
console.log(error, info);
this.setState({
isError = true
});
//也能够作其余的一些事情,如日志,异常数统计等
}
}
export default MyErrorBoundary;
复制代码
App.js
...
render(){
return (
...
<MyErrorBoundary>
<OtherComponent /> </MyErrorBoundary>
...
);
}
...
复制代码
用来设置state的值。
在16.3开始,FB建议使用setState的异步函数写法,如: 原先咱们在使用的时候是直接进行赋值:
inputHandler = (e) => {
this.setState({
inputValue: e.target.value //再也不这么作
});
)
复制代码
而是改用:
```javascript
inputHandler = (e) => {
let value = e.target.value;
this.setState(() => {
return {inputValue: value}; //使用方法的形式,最终再返回一个对象,这里须要注意下,这么写是异步的,但存在一个问题,即输入的内容,须要再方法外层先获取到:如这里的value。
})
}
```
复制代码
异步写法中,该方法提供了一个回调函数,经过该回调函数,能够确保只有等到setState触发完成以后,才会执行回调的方法(另一个能够确保在setState执行完成以后再执行的点是componentDidUpdate方法),如:
this.setState((prevState, nextState)=>{
...
}, callback);
复制代码
该方法提供了2个入参,prevState 和 props,前者至关因而 this.state。
只有当setState方法的第一个参数执行完成以后,才会执行 callback方法。
须要注意的是,由于setState是一个异步方法,因此在赋值的时候须要注意下,若是须要从表单或者其余地方获取值赋值给state的某一个属性,须要先把这个值在setState方法以前赋给一个变量,再在setState方法中使用这个变量。如
let name = e.target.value;
this.setState((prevState, props)=>{
return {
userName : name //不可以直接 userName:e.target.value,异步方法中获取不到当前上下文
}
}, callback);
复制代码
补充:对于 setState ,其不必定在调用 setState 的时候就当即触发这个动做。为了性能,react会自行判断,将组件的全部setState在同一个时间点一同执行(批量执行),而非调用一次就执行一次。
每一次 setState 都会致使组件的再次渲染,除非 shouldComponentUpdate 返回 false。