react快速入門
react元件的生命週期
在元件的整個生命週期中,隨著該元件的props或者state發生改變,其DOM表現也會有相應的變化
State 是一種資料結構,用於元件掛載時所需資料的預設值。State 可能會隨著時間的推移而發生突變,但多數時候是作為使用者事件行為的結果。 Props(properties 的簡寫)則是元件的配置。props 由父元件傳遞給子元件,並且就子元件而言,props 是不可變的(immutable)。元件不能改變自身的 props,但是可以把其子元件的 props 放在一起(統一管理)。Props 也不僅僅是資料--回撥函式也可以通過 props 傳遞。
元件的生命主要包括3個階段: 掛載、更新、解除安裝,React 16開始還新增了錯誤處理。
韋博青少微信端用的是react15版本的,先簡述下15版本的生命週期,react16的生命週期也是基於15上修改
第一個是元件初始化(initialization)階段
import React, { Component } from 'react';
class Test extends Component {
constructor(props) {
super(props);
}
}
複製程式碼
構造方法(constructor()
),Test類繼承了react Component這個基類,也就繼承這個react的基類,才能有render(),生命週期等方法可以使用
super(props)用來呼叫基類的構造方法( constructor()
), 也將父元件的props注入給子元件,供子元件讀取(元件中props只讀不可變,state可變)
而constructor()
用來做一些元件的初始化工作,如定義this.state的初始內容。
第二個是元件的掛載(Mounting)階段
此階段分為componentWillMount,render,componentDidMount三個時期
componentWillMount
:
在元件掛載到DOM前呼叫,且只會被呼叫一次,在這邊呼叫this.setState不會引起元件重新渲染,也可以把寫在這邊的內容提前到constructor()
中,所以專案中很少用。
render
:
根據元件的props和state(無兩者的重傳遞和重賦值,論值是否有變化,都可以引起元件重新render) ,return 一個React元素(描述元件,即UI),不負責元件實際渲染工作,之後由React自身根據此元素去渲染出頁面DOM。render是純函式(Pure function:函式的返回結果只依賴於它的引數;函式執行過程裡面沒有副作用),不能在裡面執行this.setState,會有改變元件狀態的副作用。
componentDidMount
:
元件掛載到DOM後呼叫,且只會被呼叫一次
1.在componentDidMount
請求非同步載入的資料
2.新增事件監聽 — Adding event listeners (or subscriptions)
第三個是元件的更新(update)階段
在講述此階段前需要先明確下react元件更新機制。
- 父元件重新render引起的props更新,更新後的state和props相對之前無論是否有變化,都將引起子元件的重新render
- setState引起的state更新,可通過
shouldComponentUpdate
方法優化。
class Child extends Component {
constructor(props) {
super(props);
this.state = {
someThings:1
}
}
handleClick = () => { // 雖然呼叫了setState ,但state並無變化
const preSomeThings = this.state.someThings
this.setState({
someThings: preSomeThings
})
}
shouldComponentUpdate(nextStates){ // 應該使用這個方法,否則無論state是否有變化都將會導致元件重新渲染
if(nextStates.someThings === this.state.someThings){
return false
}
}
render() {
return <div onClick = {this.handleClick}>{this.state.someThings}</div>
}
}
複製程式碼
componentWillReceiveProps(nextProps)
此方法只呼叫於props引起的元件更新過程中,引數nextProps是父元件傳給當前元件的新props。但父元件render方法的呼叫不能保證重傳給當前元件的props是有變化的,所以在此方法中根據nextProps和this.props來查明重傳的props是否改變,以及如果改變了要執行啥,比如根據新的props呼叫this.setState出發當前元件的重新render
shouldComponentUpdate(nextProps, nextState)
此方法通過比較nextProps,nextState及當前元件的this.props,this.state,返回true時當前元件將繼續執行更新過程,返回false則當前元件更新停止,以此可用來減少元件的不必要渲染,優化元件效能。 ps:這邊也可以看出,就算componentWillReceiveProps()中執行了this.setState,更新了state,但在render前(如shouldComponentUpdate,componentWillUpdate),this.state依然指向更新前的state,不然nextState及當前元件的this.state的對比就一直是true了。
componentWillUpdate(nextProps, nextState)
此方法在呼叫render方法前執行,在這邊可執行一些元件更新發生前的工作,一般較少用。
render
render方法在上文講過,這邊只是重新呼叫。
componentDidUpdate(prevProps, prevState)
此方法在元件更新後被呼叫,可以操作元件更新的DOM,prevProps和prevState這兩個引數指的是元件更新前的props和state
解除安裝階段
此階段只有一個生命週期方法:componentWillUnmount
componentWillUnmount
此方法在元件被解除安裝前呼叫,可以在這裡執行一些清理工作,比如清除定時器,清除componentDidMount中手動建立的DOM元素等,以避免引起記憶體洩漏。
react16 生命週期函式做的更改
舊的生命週期十分完整,基本可以捕捉到元件更新的每一個state/props/ref,沒有什麼邏輯上的毛病。
但是架不住官方自己搞事情,react打算在17版本推出新的Async Rendering,提出一種可被打斷的生命週期,而可以被打斷的階段正是實際dom掛載之前的虛擬dom構建階段,也就是要被去掉的三個生命週期。
生命週期一旦被打斷,下次恢復的時候又會再跑一次之前的生命週期, 因此componentWillMount,componentWillReceiveProps, componentWillUpdate都不能保證只在掛載/拿到props/狀態變化的時候重新整理一次了,所以這三個方法被標記為不安全。
兩個新生命週期
static getDerivedStateFromProps
觸發時間:在元件構建之後(虛擬dom之後,實際dom掛載之前) ,以及每次獲取新的props之後。
每次接收新的props之後都會返回一個物件作為新的state,返回null則說明不需要更新state.
配合componentDidUpdate,可以覆蓋componentWillReceiveProps的所有用法
class Example extends React.Component {
static getDerivedStateFromProps(nextProps, prevState) {
// 沒錯,這是一個static
}
}
複製程式碼
getSnapshotBeforeUpdate
觸發時間: update發生的時候,在render之後,在元件dom渲染之前。
返回一個值,作為componentDidUpdate的第三個引數。
配合componentDidUpdate, 可以覆蓋componentWillUpdate的所有用法。
react錯誤處理
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
複製程式碼
setState方法可能是同步的,也可能是非同步的
由 React 控制的事件處理過程 setState 不會同步更新 this.state
基於效能考慮,React 通常是批量合併更新,呼叫 setState() 之後,this.state 並沒有馬上修改,而是建立了一箇中間態作為過渡。
但是有些例外情況,它是同步執行的,比如:eventListeners,Ajax,setTimeout 等。 原因是這些 JS 原生的 API 不在 React 的上下文控制範圍,無法進行優化。
this.setState(
{ count: 1 }, () => {
console.log(this.state.count)//輸出count=1
}
)
複製程式碼
react事件繫結
由於類的方法預設不會繫結this,因此在呼叫的時候如果忘記繫結,this的值將會是undefined。 通常如果不是直接呼叫,應該為方法繫結this。繫結方式有以下幾種:
- 在建構函式中使用bind繫結this
class Button extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
複製程式碼
- 在呼叫的時候使用bind繫結this
class Button extends React.Component {
handleClick(){
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick.bind(this)}>
Click me
</button>
);
}
}
複製程式碼
- 在呼叫的時候使用箭頭函式繫結this
class Button extends React.Component {
handleClick(){
console.log('this is:', this);
}
render() {
return (
<button onClick={()=>this.handleClick()}>
Click me
</button>
);
}
}
複製程式碼
- 使用屬性初始化器語法繫結this(實驗性)
class Button extends React.Component {
handleClick=()=>{
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
複製程式碼
方式1是官方推薦的繫結方式,也是效能最好的方式。方式2和方式3會有效能影響並且當方法作為屬性傳遞給子元件的時候會引起重渲問題。方式4目前屬於實驗性語法,但是是最好的繫結方式,需要結合bable轉譯
react列表渲染時為什麼儘量不要把索引設定為key值
使用陣列下標做key是不被推薦的,如果遇到陣列排序的情況下,將降低渲染效能。
React中的核心概念
-
虛擬DOM(Virtual DOM) github.com/livoras/blo…
-
Diff演算法(虛擬DOM的加速器,提升React效能的法寶) github.com/zmmbreeze/b…