在上篇文章React 導讀(一)中學習到了寫第一個 Web 組件,這篇文章將繼續以前的目錄,開始新的知識點補充:javascript
其實在學習 React 以前,就應該瞭解目前前端推薦的是組件化開發的方式,React 是讓組件化更加簡單的庫。那麼組件開發必不可少的就是生命週期,說直白一點就是運行組件的過程是 React 來作,運行過程當中須要有一些代碼鉤子來讓咱們去調用,在組件執行的某一個地方去執行咱們本身寫的代碼。這裏先介紹擁有的生命週期鉤子,下面的方法 constructor
和 render
不屬於生命週期,我按功能分類了一下,也就是學習的時候不必定要循序漸進,應該以學習以後能真正寫一些東西爲目標:前端
(1) 與組件掛載相關的方法,包括構造函數java
最經常使用的生命週期應該是最後 2 個,constructor
和 componentWillMount
目前先理解成能知足的功能大致相同,若是這裏解釋太複雜不太好。ajax
對於最開始關注的是:this.state
的初始化以及 ajax
在哪裏請求。typescript
this.state
在 constructor 進行初始化,ajax
推薦在componentDidMount
中進行請求。
componentDidMount
就是在組件已經掛載到 DOM 中後的鉤子,能夠理解爲 jQuery 中提供的 ready
方法。componentWillUnmount
是在組件即將被卸載前一刻的鉤子,通常用於取消 componentDidMount
中訂閱的事件等做用,清理一些不要的變量等,避免內存泄漏。下面經過一個簡單的例子說明一下:編程
先有一個 foods.json
文件來模擬請求的後臺數據。json
[ { "id": 1, "name": "香蕉" }, { "id": 2, "name": "蘋果" }, { "id": 3, "name": "獼猴桃" } ]
// 1. 掛載相關的生命週期 class CycleMount extends React.Component { constructor() { super(); this.state = { foods: [] }; console.log('1. constructor 執行了...'); } componentDidMount() { // 這裏使用原生的 fetch API 進行 ajax 請求,你也可使用 $.ajax 進行請求,原理是同樣的,重點是關注 setState 的地方 fetch('/mock/foods.json', { method: 'GET', headers: new Headers({ 'Accept': 'application/json' }) } ).then(dataResult => { if(dataResult.status === 200) { return dataResult.json(); } else { return []; } }).then(data => { // 這裏的 data 就是 foods.json 裏面的數據 // 調用 setState 來更新 render 裏面調用的 this.state 的值 this.setState({ foods: data }); }); console.log('2. componentDidMount 執行了...'); } render() { // foods 是一個數組,map 方法是數組自帶的方法,能夠查詢相關 api const foodItems = this.state.foods.map(food => { return (<li key={food.id}>{food.name}</li>); }); // 這裏是返回的最終組件結構 return ( <ul> {foodItems} </ul> ); } }
上面有了完整的註釋,也能看到基本上項目中可能會將代碼寫到何處,我也打了兩個日誌,來識別究竟是誰先執行,結果能夠本身運行一下,執行順序就是我標記的1,2。api
好了,基本的學習了,能夠本身動手試試訂閱一個事件,而後在卸載的時候取消這個事件。數組
(2) 優化相關app
這個方法比較重要,可是我這裏不會介紹得太過於複雜,太複雜只會讓重要的部分不那麼突出。這個方法的返回值是一個 boolean
類型,分別代碼的意義:
true
組件應該更新,執行 render
方法以及相關生命週期;false
組件狀態沒有更新,不執行 render
等方法,意味着網頁界面不會改變。那麼它直觀上的做用是可以經過返回值來決定界面是否改變,實際的意義就是當咱們知道當前 oldState = this.state
的值和新的 newState = this.state
值徹底相等的時候(或者是新傳入的 props)就不用再浪費性能去從新渲染組件了。
API 上的定義是這樣的:
/* nextProps: 新的 props, nextState: 新的 state */ shouldComponentUpdate(nextProps, nextState): boolean
舉個例子來直觀說明一下:
class ComponentOptimize extends React.Component { state = { count: 0 }; shouldComponentUpdate(nextProps, nextState) { // 當 count > 10 的時候就不能再從新渲染組件了 if(nextState.count > 10) { return false; } return true; } handleUpdateCount() { console.log('我點了一下哦!'); this.setState(prevState => { return { count: prevState.count + 1 }; }); } render() { return ( <div onClick={this.handleUpdateCount.bind(this)} style={{cursor: 'pointer'}}> <h1>{this.state.count}</h1> </div> ); } }
你會發現 10 事後界面就沒有再更新過了,這樣應該很直觀了。
(3) Props 相關的生命週期
這個主要是在組件的 props 傳入新的值後被調用,不論是不是傳的同樣的值或者 shouldComponentUpdate
返回了 false
,看下例子吧:
class Cat extends React.Component { componentWillReceiveProps(nextProps) { console.log('改一次我執行一次!'); } shouldComponentUpdate(nextProps, nextState) { // 改的名字同樣的時候 return this.props.name !== nextProps.name; } render() { console.log('貓改了一次名字!'); return ( <h1>我有新名字了!{this.props.name}</h1> ); } } class App extends React.Component { state = { catName: '嚕嚕' }; handleChangeCatName() { const catNames = ['嚕嚕', '小白', '小黃', '小黑', '皮卡丘']; const catIndex = this.getSomeOneIndex(); this.setState({ catName: catNames[catIndex] }); } getSomeOneIndex() { return Math.floor(Math.random() * 5 + 0); } render() { return ( <div> {/* 給 Cat 傳新的名字 */} <Cat name={this.state.catName} /> <button onClick={this.handleChangeCatName.bind(this)}>點我給貓咪取新名字!</button> </div> ); } }
最後確定每次點擊按鈕都會輸出這句的結果 console.log('改一次我執行一次!');
。
(4) 更新組件相關
由於都是講解 API,因此國際慣例的先看下 API 的定義吧:
// 組件更新前執行 componentWillUpdate(nextProps, nextState) // 組件更新後執行 componentDidUpdate(prevProps, prevState)
能夠從定義中看出,它們都接受了兩個參數:props && state,不過看變量前綴可以聯想點什麼。
暫時想不到什麼實際項目的例子,隨便假設點內容吧。不過這裏須要注意的地方是:
第一條的緣由:容易造成一個遞歸的調用,不做就不會死...因此儘可能不要在這裏調~目前尚未碰到須要在這裏調的需求。
第二條的緣由:額,說好的更新才調,初始化不調用是符合邏輯的。
第三條的緣由:額,這 2 個鉤子是與組件更新相關的,因此也符合邏輯的,組件是否更新就是靠 shouldComponentUpdate
返回值。
在上面 Cat
的例子中加入下面的代碼能夠看下結果:
componentWillUpdate() { console.log('componentWillUpdate 執行了!') } componentDidUpdate() { console.log('componentDidUpdate 執行了!') }
(5)組件錯誤
就是在組件發生異常的時候可能會被調用的鉤子,須要注意的有下面的地方:
try...catch
包裹父級組件的鉤子就不會執行了。看個例子吧:
class Cat extends React.Component { componentWillReceiveProps(nextProps) { // 這裏手動拋一個異常,觸發咱們的鉤子 componentDidCatch throw new Error('miao miao~'); } render() { let miao = this.props.name; return ( <div> {miao} </div> ); } } class App extends React.Component { state = { catName: '嚕嚕', isError: false, }; handleChangeCatName() { const catNames = ['嚕嚕', '小白', '小黃', '小黑', '皮卡丘']; const catIndex = this.getSomeOneIndex(); this.setState({ catName: catNames[catIndex] }); } getSomeOneIndex() { return Math.floor(Math.random() * 5 + 0); } componentDidCatch(error, info) { console.log(error, info); if(error) { // 若是有錯誤信息,就從新渲染一下組件,多是更好的交互 this.setState({ isError: true }); } } render() { return ( <div> <Cat name={this.state.catName} /> {!this.state.isError ? <button onClick={this.handleChangeCatName.bind(this)}>點我給貓咪取新名字!</button> : <p>不要奴才給我取名字了!</p> } </div> ); } }
(6) 渲染相關
額...這個不想寫了,先睡覺吧~應該寫了這麼多個小例子也差很少了~能夠動手試試哦!還不清楚的能夠 Google 一下,你就知道。
生命週期很重要,其實學到這裏也差很少能夠上手寫點項目熟練一下了,其餘的更可能是思惟和編程方面的東西,週期的篇幅單獨來一篇吧~其餘主題以後再繼續吧!