React 深入系列4:组件的生命周期
文:徐超,《React进阶之路》作者 React 深入系列4:组件的生命周期React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。 组件是构建React应用的基本单位,组件需要具备数据获取、业务逻辑处理、以及UI呈现的能力,而这些能力是要依赖于组件不同的生命周期方法的。组件的生命周期分为3个阶段:挂载阶段、更新阶段、卸载阶段,每个阶段都包含相应的生命周期方法。因为是深入系列文章,本文不会仔细介绍每个生命周期方法的使用,而是会重点讲解在使用组件生命周期时,经常遇到的疑问和错误使用方式。 服务器数据请求初学者在使用React时,常常不知道何时向服务器发送请求,获取组件所需数据。对于组件所需的初始数据,最合适的地方,是在componentDidMount方法中,进行数据请求,这个时候,组件完成挂载,其代表的DOM已经挂载到页面的DOM树上,即使获取到的数据需要直接操作DOM节点,这个时候也是绝对安全的。有些人还习惯在constructor或者componentWillMount中,进行数据请求,认为这样可以更快的获取到数据,但它们相比componentDidMount的执行时间,提前的时间实在是太微乎其微了。另外,当进行服务器渲染时(SSR),componentWillMount是会被调用两次的,一次在服务器端,一次在客户端,这时候就会导致额外的请求发生。 组件进行数据请求的另一种场景:由父组件的更新导致组件的props发生变化,如果组件的数据请求依赖props,组件就需要重新进行数据请求。例如,新闻详情组件NewsDetail,在获取新闻详情数据时,需要传递新闻的id作为参数给服务器端,当NewsDetail已经处于挂载状态时,如果点击其他新闻,NewsDetail的componentDidMount并不会重新调用,因而componentDidMount中进行新闻详情数据请求的方法也不会再次执行。这时候,应该在componentWillReceiveProps中,进行数据请求: componentWillReceiveProps(nextProps) { if(this.props.newId !== nextProps.newsId) { fetchNewsDetailById(nextProps.newsId) // 根据最新的新闻id,请求新闻详情数据 } } 如果进行数据请求的时机是由页面上的交互行为触发的,例如,点击查询按钮后,查询数据,这时只需要在查询按钮的事件监听函数中,执行数据请求即可,这种情况一般是不会有疑问的。 更新阶段方法的调用组件的更新是组件生命周期中最复杂的阶段,也是涉及到最多生命周期方法的阶段。 正常情况下,当组件发生更新时,组件的生命周期方法的调用顺序如下: componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate // 组件收到新的props(props中的数据并不一定真正发生变化)-> 决定是否需要继续执行更新过程 -> 组件代表的虚拟DOM即将更新 -> 组件重新计算出新的虚拟DOM -> 虚拟DOM对应的真实DOM更新到真实DOM树中 父组件发生更新或组件自身调用setState,都会导致组件进行更新操作。父组件发生更新导致的组件更新,生命周期方法的调用情况同上所述。如果是组件自身调用setState,导致的组件更新,其生命周期方法的调用情况如下: shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate 可见,这种情况下componentWillReceiveProps并不会被调用。 当组件的shouldComponentUpdate返回false时,组件会停止更新过程,这时候生命周期方法的调用顺序如下: componentWillReceiveProps -> shouldComponentUpdate -> 结束 或(组件自身调用setState,导致的组件更新): shouldComponentUpdate -> 结束 setState的时机组件的生命周期方法众多,哪些方法中可以调用setState更新组件状态?哪些方法中不可以呢?
render次数 != 浏览器界面更新次数先看下面的一个例子: class App extends React.Component { constructor(props) { super(props) this.state = { bgColor: "red" } } render() { var {bgColor} = this.state return ( <div style = {{backgroundColor: bgColor}}> Test </div> ); } componentDidMount() { this.setState({ bgColor: "yellow" }) } } 当我们观察浏览器渲染出的页面时,页面中Test所在div的背景色,是先显示红色,再变成黄色呢?还是直接就显示为黄色呢? 答案是:直接就显示为黄色! 这个过程中,组件的生命周期方法被调用的顺序如下: constructor -> componentWillMount -> render -> componentDidMount -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate 组件在挂载完成后,因为setState的调用,将立即执行一次更新过程。虽然render方法被调用了两次,但这并不会导致浏览器界面更新两次,实际上,两次DOM的修改会合并成一次浏览器界面的更新。React官网介绍componentDidMount方法时也有以下说明: Calling 这说明,组件render的次数 不一定等于 浏览器界面更新次数。虽然JS的执行和DOM的渲染分别由浏览器不同的线程完成,但JS的执行会阻塞DOM的渲染,而上面的两次render是在一个JS事件周期内执行的,所以在两次render结束前,浏览器不会更新界面。 下篇预告:React 深入系列5:事件处理 新书推荐《React进阶之路》 作者:徐超 毕业于浙江大学,硕士,资深前端工程师,长期就职于能源物联网公司远景智能。8年软件开发经验,熟悉大前端技术,拥有丰富的Web前端和移动端开发经验,尤其对React技术栈和移动Hybrid开发技术有深入的理解和实践经验。
美团点评广告平台大前端团队招收20192020年前端实习生(偏动效方向) 有意者邮件:yao.zhou@meituan.com (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |