一文搞懂ReactNative生命周期的进化

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"众所周知每个应用的开发框架都有其对应的生命周期函数,ReactNative是基于React开发的,所以其生命周期先关函数也和React一样密不可分,为什么文章标题叫“生命周期的进化”呢? 这是有原因的,因为React在React 15和React 16两个版本对生命周期函数做了优化调整,到底进行了那些调整和改进呢? 让我们随着本文一探究竟。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"React 15生命周期函数"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面这张图是一个典型的React 15的生命周期函数流程图,也是我们大多数开发者所了解到的。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2b/2b1723ffdc799ee307781227cb0d8a3a.png","alt":"在这里插入图片描述","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"React 15相关的生命周期函数如下:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"constructor()\ncomponentWillReceiveProps()\nshouldComponentUpdate()\ncomponentWillMount()\ncomponentWillUpdate()\ncomponentDidUpdate()\ncomponentDidMount()\nrender()\ncomponentWillUnmount()"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Mounting阶段:组件初始化渲染"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"初始化渲染阶段主要涉及如下几个生命周期函数:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"constructor()\ncomponentWillMount()\nrender()\ncomponentDidMount()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"constructor()这个函数只在组件初始化的调用一次,通常开发中主要用于对state的数据初始化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"componentWillMount(),componentDidMount()函数在组件初始化阶段也只调用一次,componentWillMount()在render()函数执行之前被调用,有些同学开发中会在此函数中做网络请求,想想会有哪些风险呀?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之后触发render()方法进行渲染页面,但此时不会去操作真实DOM,只是把要渲染的页面返回处理,真正处理是通过ReactDOM.render方法在页面挂在阶段完成的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"componentDidMount()函数会在真实DOM挂载完成时候调用,通常开发中我们在此函数进行网络请求,消息监听等来操作真实的DOM。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Updating阶段:组件更新"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"组件的更新分为两种:一种是父组件的更新触发的更新,另一种是组件自身调用this.setState()触发的更新。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"组件更新过程中涉及的如下生命周期函数:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"componentWillReceiveProps()\nshouldComponentUpdate()\ncomponentWillUpdate()\nrender()\ncomponentDidUpdate()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先看一下componentWillReceiveProps(nextProps),这个函数参数中nextProps表示接受到的新props,可以用来和this.props进行对比,看是否改变。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"请注意,如果父组件导致组件重新渲染,即使 props 没有更改,也会调用此方法(componentWillReceiveProps)。如果只想处理更改,请确保进行当前值与变更值的比较。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">------ React官方文档"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关键点:也就是componentWillReceiveProps()函数不是props改变触发的,而是由于父组件更新触发的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"shouldComponentUpdate(nextProps, nextState)函数在组件自身的state发生改变时触发,该函数的返回值决定了组件是否重新render,默认返回“true”,表示只要是state发生更新,就会重新render;实际开发工作中,我们一般会对比函数中的参数来进行业务逻辑判断是否需要重新render。这也是一个React提供给我们的一个性能优化的方向之一。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"componentWillUpdate()与componentDidUpdate()是在render前后进行触发的,对应于componentWillMount()和componentDidMount()。componentDidUpdate在组件更新完成之后触发,可以操作真实DOM。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Unmounting阶段:组件卸载"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个阶段就比较简单了,只有一个生命周期函数:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"componentWillUnmount()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在组件卸载和销毁之前会触发componentWillUnmount()函数,实际开发中我们通常进行如下操作,比如清除定时器timer, 取消网络请求或者清除在 componentDidMount() 中创建的订阅等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"进化:React 16生命周期函数"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面节选我们回顾了React 15的生命周期函数,那么React 16有那些更新和改进呢? 其中16.3版本和16.4版本的生命周期稍有不同,首先我们一起来16.3版本的流程图"},{"type":"link","attrs":{"href":"https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/","title":""},"content":[{"type":"text","text":"React 16.3 Lifecycle"}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/db/dbfb1f4c556a5cdad6807e80c2133095.png","alt":"在这里插入图片描述","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"设计到的生命周期函数如下:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"constructor()\ngetDerivedStateFromProps()\ngetSnapshotBeforeUpdate()\nshouldComponentUpdate()\ncomponentDidUpdate()\ncomponentDidMount()\nrender()\ncomponentWillUnmount()"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Mounting阶段:组件初始化阶段(挂载)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在React 16的mounting阶段涉及到的生命周期函数如下:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"constructor()\ngetDerivedStateFromProps()\ncomponentDidMount()\nrender()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"React 16和React 15相比在初始化阶段,多了一个getDerivedStateFromProps()函数,少了一个componentWillMount()函数。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那getDerivedStateFromProps()是用来替换componentWillMount()函数的吗?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先我们看一下这个函数"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"static getDerivedStateFromProps(props, state)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这是一个静态的函数,并且需要返回一个对象用来更新state,如果返回null,则不会更新state。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关于此函数需要了解三个关键重要的点。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一个这是一个静态函数,所以在这个函数中不能访问class的实例,也就是说不能在函数中使用this通过this.props的获取数据。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二个此函数接受两个参数props和state,props是指父组件传递的props,state指的是自身的state。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第三个此函数需要一个对象格式的返回值,因为需要根据这个返回的数据来更新state,如果不需要更新state,就返回一个null, 如果什么都不返回,就会收到警告,或者干脆不重新这个函数。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Updating阶段:组件更新阶段"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在更新阶段涉及到的函数有一下几个:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"getDerivedStateFromProps()\nshouldComponentUpdate()\nrender()\ngetSnapshotBeforeUpdate()\ncomponentDidUpdate()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在更新阶段,16.3和16.4稍微有些不同,来看一下流程图"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6d/6d4fafa65b21dfe5e9087d7901eb1706.png","alt":"在这里插入图片描述","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对比16.3和16.4的流程图我们发现,变化的部分是getDerivedStateFromProps()的触发时机,主要的变化体现在"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下两个方面:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.React 16.4版本中getDerivedStateFromProps()在父组件更新接受props,组件自身调用setState()函数以及forceUpdate()函数执行时都会被触发"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.React 16.3在更新阶段只有父组件更新才会触发。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对比React 15,我们发现React 16版本少了componentWillReceiveProps()以及componentWillUpdate(),增加了getDerivedStateFromProps()和getSnapshotBeforeUpdate()。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们知道React 15中父组件发生更新后会触发componentWillReceiveProps()函数,而自身setState的时候不会触发,而在React 16中组件任何更新都会触发getDerivedStateFromProps()函数,这种改进是为什么呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于getDerivedStateFromProps,React给出的解释是:"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"与 componentDidUpdate 一起,这个新的生命周期涵盖过时componentWillReceiveProps 的所有用例。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看出这个新函数不仅仅是componentWillReceiveProps的简单替代,其方法被定义成了static,即在方法内部能不能访问this,那么就能够避免错误的操作,比如使用this.fetch进行网络请求,使用this.setSate进行render造成死循环。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此getDerivedStateFromProps 的存在只有一个目的:让组件在 props 变化时更新 state。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关于此函数的使用,官方也给出了建议:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意: 旧的 componentWillReceiveProps 和新的 getDerivedStateFromProps"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"方法都会给组件增加明显的复杂性。这通常会导致 bug。考虑 "},{"type":"link","attrs":{"href":"https://zh-hans.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html","title":""},"content":[{"type":"text","text":"派生 state 的简单替代方法"}]},{"type":"text","text":" 使组件可预测且可维护。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来看getSnapshotBeforeUpdate,React官方的解释:"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"与 componentDidUpdate 一起,这个新的生命周期涵盖过时的 componentWillUpdate 的所有用例。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"getSnapshotBeforeUpdate(prevProps, prevState)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在这个函数里面可以拿到更新前的真实DOM和更新后的最新props和state."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后看一下componentDidUpdate函数"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"componentDidUpdate(prevProps, prevState, snapshot)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果组件实现了 getSnapshotBeforeUpdate() 生命周期(不常用),则它的返回值将作为 componentDidUpdate() 的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Unmounting阶段:组件卸载"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在卸载阶段React 16和React 15一样,没有发生变化,都只涉及一个生命周期函数:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"componentWillUnmount()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"componentWillUnmount() 中不应调用 setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"生命周期进化的原因"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"React 的生命周期发生变化和改进的原因都是因为React 项目使用成为“Fiber”的核心架构重写了React。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Fiber使原来React 15的同步渲染变成了异步渲染,避免阻塞React 主线程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在React 16之前,我们使用setState更新组件,React都会生成一个新的虚拟DOM,通过与上一次的DOM进行diff对比后,再定向更新真实的DOM。这是一个同步渲染的递归过程,就如同走楼梯一样。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f3/f317e434ddc866e8b8555f2b04823cf4.png","alt":"在这里插入图片描述","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果页面的布局复杂嵌套很深,那么递归调用的时间就会很长,那么的主线程就会被js一直占用着,任何交互,布局,渲染都会停止,那给用户呈现的画面就是很卡顿。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而使用Fiber重构之后就解决了这个问题,Fiber将漫长的更新任务进行切片成小任务。执行完一个小任务,就将主线程交换回去,看看是否有优先级更高的任务需要处理,这样就避免了同步更新造成UI阻塞的问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用Fiber进行切片后,异步的渲染任务就变成了可打断的,执行过程就变成了如下的模式:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a7/a7dfff1875576aeb840f241e9549d886.png","alt":"在这里插入图片描述","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"使用Fiber背后的故事"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在React 16以后,如下的几个生命周期函数都被标记为不安全的,"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"componentWillMount\ncomponentWillReceiveProps\ncomponentWillUpdate"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在这几个生命周期函数前都增加了“UNSAFE_”前缀,变成如下模样:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"UNSAFE_componentWillMount、\nUNSAFE_componentWillReceiveProps \nUNSAFE_componentWillUpdate"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为Fiber重构后,渲染变成了异步的,通过查看新的生命周期图谱,这几个方法都处于原来的render阶段,也就是会出现重复调用的问题,比如说不合理的使用setState造成重复渲染死循环等。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"总结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"总的来说,React生命周期的进化都是为Fiber架构服务的,Fiber带了异步渲染的机制,使生命周期变的更加纯粹和可控,同时也减少了我们书写代码不规范造成的不必要的bug。"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章