摘要:一個組件的生命周期分為三個部分實例化存在期和銷毀時。如果回調(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渲染一個