摘要:一個組件的生命周期分為三個部分實例化存在期和銷毀時。如果回調(diào)函數(shù)以函數(shù)的方式來指定,那么在組件更新的時候回調(diào)會被調(diào)用次。
一個React組件的生命周期分為三個部分:實例化、存在期和銷毀時。
實例化階段
客戶端渲染時,如下依次被調(diào)用
getDefaultProps()
getInitialState()
componentWillMount()
render()
componentDidMount()
服務(wù)端渲染
getDefaultProps()
getInitialState()
componentWillMount()
render()
注意:componentDidMount()不會再服務(wù)端被渲染;
getDefaultProps
對于每個組件實例來講,這個方法只會調(diào)用一次,該組件類的所有后續(xù)應(yīng)用,getDefaultPops 將不會再被調(diào)用,其返回的對象可以用于設(shè)置默認的props值。
var Hello = React.creatClass({ getDefaultProps: function(){ return { name: "pomy", git: "dwqs" } }, render: function(){ return (Hello,{this.props.name},git username is {this.props.dwqs}) } }); ReactDOM.render(, document.body);
也可以在掛載組件的時候設(shè)置 props。
var data = [{title: "Hello"}];
或者調(diào)用 setProps (一般不需要調(diào)用)來設(shè)置其 props
var data = [{title: "Hello"}]; var Hello = React.render(, document.body); Hello.setProps({data:data});
但只能在子組件或組件樹上調(diào)用 setProps。別調(diào)用 this.setProps 或者 直接修改 this.props。將其當做只讀數(shù)據(jù)。
React通過 propTypes 提供了一種驗證 props 的方式,propTypes 是一個配置對象,用于定義屬性類型:
var survey = React.createClass({ propTypes: { survey: React.PropTypes.shape({ id: React.PropTypes.number.isRequired }).isRequired, onClick: React.PropTypes.func, name: React.PropTypes.string, score: React.PropTypes.array ... }, //... })
或者
import React, { Component } from "react" import PropTypes from "prop-types" class BetterImage extends Component{...} BetterImage.PropTypes={ src: PropTypes.string, center: PropTypes.bool, loadingImage: PropTypes.string, defaultImage: PropTypes.string, onLoad: PropTypes.func, onError: PropTypes.func, onComplete: PropTypes.func } BetterImage.defaultProps={ .... }
getInitialState
對于組件的每個實例來說,這個方法的調(diào)用有且只有一次,用來初始化每個實例的 state,在這個方法里,可以訪問組件的 props。每一個React組件都有自己的 state,其與 props 的區(qū)別在于 state只存在組件的內(nèi)部,props 在所有實例中共享。
getInitialState 和 getDefaultPops 的調(diào)用是有區(qū)別的,getDefaultPops 是對于組件類來說只調(diào)用一次,后續(xù)該類的應(yīng)用都不會被調(diào)用,而 getInitialState 是對于每個組件實例來講都會調(diào)用,并且只調(diào)一次。
var LikeButton = React.createClass({ //初始化State getInitialState: function() { return {liked: false}; }, handleClick: function(event) { //設(shè)置修改State this.setState({liked: !this.state.liked}); }, render: function() { var text = this.state.liked ? "like" : "haven"t liked"; return (You {text} this. Click to toggle.
); } }); ReactDOM.render(, document.getElementById("example") );
每次修改 state,都會重新渲染組件,實例化后通過 state 更新組件,會依次調(diào)用下列方法:
1、shouldComponentUpdate 2、componentWillUpdate 3、render 4、componentDidUpdate
componentWillMount
在渲染前調(diào)用,在客戶端也在服務(wù)端。React 官方正式發(fā)布了 v16.3 版本。在這次的更新中,除了前段時間被熱烈討論的新 Context API 之外,新引入的兩個生命周期函數(shù) getDerivedStateFromProps,getSnapshotBeforeUpdate 以及在未來 v17.0 版本中即將被移除的三個生命周期函數(shù) componentWillMount,componentWillReceiveProps,componentWillUpdate .
在這個生命周期中你會遇到一下問題:
a.首屏無數(shù)據(jù)導致白屏
在 React 應(yīng)用中,許多開發(fā)者為了避免第一次渲染時頁面因為沒有獲取到異步數(shù)據(jù)導致的白屏,而將數(shù)據(jù)請求部分的代碼放在了 componentWillMount 中,希望可以避免白屏并提早異步請求的發(fā)送時間。但事實上在 componentWillMount 執(zhí)行后,第一次渲染就已經(jīng)開始了,所以如果在 componentWillMount 執(zhí)行時還沒有獲取到異步數(shù)據(jù)的話,頁面首次渲染時也仍然會處于沒有異步數(shù)據(jù)的狀態(tài)。換句話說,組件在首次渲染時總是會處于沒有異步數(shù)據(jù)的狀態(tài),所以不論在哪里發(fā)送數(shù)據(jù)請求,都無法直接解決這一問題。而關(guān)于提早發(fā)送數(shù)據(jù)請求,官方也鼓勵將數(shù)據(jù)請求部分的代碼放在組件的 constructor 中,而不是 componentWillMount。
若是為了改善用戶體驗曾經(jīng)用過的解決方法有兩個:
方法一:異步請求組件,使用nprogress 添加加載動畫;
import React, { Component } from "react" import NProgress from "nprogress" import "nprogress/nprogress.css" import "./customNprogress.styl" NProgress.configure({ showSpinner: false }) export default function asyncComponent(importComponent) { class AsyncComponent extends Component { state = { component: null } async componentDidMount() { NProgress.start() const { default: component } = await importComponent() NProgress.done() this.setState({ component }) } render() { const C = this.state.component return C ?: null } } return AsyncComponent } const AsyncNotFound = asyncComponent(() => import(/* webpackChunkName: "NotFound" */ "@/routes/NotFound"))
方法二:使用 onreadystatechange 去監(jiān)聽 readyState,在資源加載完成之前加載一個只有框架的靜態(tài)頁面,頁面不請求數(shù)據(jù)。當數(shù)據(jù)請求完成之后再將路由切換到真實的首頁。
function listen () { if (document.readyState == "complete") { // 資源加載完成 ReactDom.render(, document.getElementById("root") ) } else { // 資源加載中 ReactDom.render( , document.getElementById("root") ) } } document.onreadystatechange = listen
具體參考解決React首屏加載白屏的問題
b.事件訂閱
另一個常見的用例是在 componentWillMount 中訂閱事件,并在 componentWillUnmount 中取消掉相應(yīng)的事件訂閱。但事實上 React 并不能夠保證在 componentWillMount 被調(diào)用后,同一組件的 componentWillUnmount 也一定會被調(diào)用。一個當前版本的例子如服務(wù)端渲染時,componentWillUnmount 是不會在服務(wù)端被調(diào)用的,所以在 componentWillMount 中訂閱事件就會直接導致服務(wù)端的內(nèi)存泄漏。另一方面,在未來 React 開啟異步渲染模式后,在 componentWillMount 被調(diào)用之后,組件的渲染也很有可能會被其他的事務(wù)所打斷,導致 componentWillUnmount 不會被調(diào)用。而 **componentDidMount 就不存在這個問題,在 componentDidMount 被調(diào)用后,componentWillUnmount 一定會隨后被調(diào)用到,并根據(jù)具體代碼清除掉組件中存在的事件訂閱。**
render
該方法會創(chuàng)建一個虛擬DOM,用來表示組件的輸出。對于一個組件來講,render方法是唯一一個必需的方法。render方法需要滿足下面幾點:
只能通過 this.props 和 this.state 訪問數(shù)據(jù)(不能修改)
可以返回 null,false(這種場景下,react渲染一個