1. 程式人生 > >react生命週期,中介軟體、效能優化、資料傳遞、mixin的使用

react生命週期,中介軟體、效能優化、資料傳遞、mixin的使用

https://github.com/lulujianglab/blog/issues/34

一、生命週期

1,初始化的執行順序,初始生命週期執行過程詳解

class initSate extends Component { constructor(props, context) { super() this.state = {} } // 只調用一次,例項之間共享引用 getDefaultProps() { } // 初始化每個例項特有的狀態 getInitialState() { } // render之前最後一次修改的機會
componentWillMount() { } // 只能訪問到this.props、this.state,只是一個頂層元件,不允許修改狀態和Dom輸出 render() { return ( <div></div> ) } // 成功render並渲染完成真實的DOM之後觸發,可以修改Dom componentDidMount() { } } export default initSate
2,執行中階段可以使用的函式,執行中的順序   class runSate extends Component { constructor(props, context) { super() this.state = {} } // 父元件修改屬性觸發,可以修改新屬性、修改狀態 componentWillReceiveProps() { } // 用於元件優化,返回false會防止render呼叫
shouldComponentUpdate() { } // 不能修改屬性和狀態 componentWillUpdate() { } // 只能訪問到this.props、this.state,只是一個頂層元件,不允許修改狀態和Dom輸出 render() { return ( <div></div> ) } // 可以修改Dom componentDidUpdate() { } } export default runSate 3,摧毀階段使用的函式的 class destroySate extends Component { constructor(props, context) { super() this.state = {} } // 在刪除元件之前進行清理操作,比如計時器和事件監聽器 componentWillUnmount() { } // 只能訪問到this.props、this.state,只是一個頂層元件,不允許修改狀態和Dom輸出 render() { return ( <div></div> ) } } export default destroySate   二、react的資料傳遞—單項資料流 1, 父--子------父傳遞屬性,子—this.props.num 2, 子--父-------子:onChange={this.props.handleEmail}—父:handleEmail(){} 例子省略 三,react除錯原理 效能存在問題: 1,父元件更新預設觸發所有子元件的更新 2,列表型別元件預設更新方式複雜 解決方法: 子元件覆蓋shoudComponentUpdate的方法,自行判斷是否更新 給列表元件新增key屬性 如圖所示:

 

http://confluence.daojia-inc.com/pages/viewpage.action?pageId=85659735 控制檯的監控效能解析 1,React.addons.start():開始 2,   React.addons.stop():結束 3,分析結果 React.addons.perf.printInclusive() 四,公共元件方法拆分----pureRenderMixin的介紹 // 主檔案---混入mixin const BannerListM = mixinHoc(BannerList) // mixinHoc檔案
export default function mixinHoc(importComponent) {
class MixinHoc extends Component {
constructor(props) { super(props) }
componentWillUnmount() { const { setDialogShow } = this.props // 重置關閉Dialog彈出框 setDialogShow(false) }
render() { const Component = importComponent
return Component ? <Component {...this.props} /> : null } }
// redux state對映處理 const mapStateToProps = (state) => { return {} }
// redux dispatch對映處理 const mapDispatchToProps = (dispatch, ownProps) => { return { setDialogShow(data) { dispatch(setDialogShow(data)) } } }
return connect(mapStateToProps, mapDispatchToProps)(MixinHoc) } 五,中間間的講解

react 本質上是一個 JavaScript 的庫,是建立UI介面的檢視層框架

(圖一)

image

如圖一所示,假如藍色元件需要和灰色元件通訊,只使用 react 檢視層框架,就需要呼叫父元件函式的形式通訊,逐層往父級通訊

但對於大型應用來說,這樣實現基本不太可能,過多的元件會造成維護困難,那應該怎麼做呢?

這個時候就應該在 react 檢視層框架上配套一個數據層框架 — Redux ,結合應用

redux 要求我們把資料都放在 store 公共儲存空間,當綠色元件想要去傳遞資料時,只需要改變 store 裡邊對應的資料,灰色區域會自動感知到 store 有變化,就會重新去 store 取資料,從而灰色元件就能得到新的資料

這樣的操作流程對於深層次的元件是非常適用的,元件與元件之間的資料傳遞會變得非常簡單

元件改變,修改資料,其他元件再來取值。這就是 Redux 的基礎設計理念

使用 Redux-thunk 中介軟體進行ajax請求傳送

如果我們把這種非同步的請求,或者把一些非常複雜的邏輯都放在元件裡進行實現時,這個元件會顯得過於臃腫

所以遇到這種非同步請求或者非常複雜的邏輯,最好是把它移出到其他頁面進行統一的處理,

這個時候 Redux-thunk 這個中介軟體就顯得至關重要了,它可以將這些非同步請求或者是複雜的邏輯放到 action 去處理,那如何使用 Redux-thunk 這個中介軟體呢?

開啟github,搜尋 Redux-thunk ,star最多的專案,就是Redux-thunk

按照它的使用說明進行如下操作

import { createStore, applyMiddleware } from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'

const store = createStore(
  reducer,
  applyMiddleware(thunk) // applyMiddleware可以使用中介軟體模組
) 

export default store

需要注意的是:

  1. 中介軟體是通過建立 redux 的 store 時使用的,所以這個中介軟體是指的 redux 中介軟體,而不是 react 中介軟體

  2. 原則上 action 返回的是一個物件,但當我們使用 redux-thunk 中介軟體後, action 就可以返回一個函數了,繼而可以在函式裡邊進行非同步操作,也就可以把 TodoList 獲取資料的請求放入這個函式中了

接著操作,在 actionCreator 中建立 action 的函式,然後資料傳給 store

那問題來了,怎麼傳呢?本質還是呼叫 dipatch 方法,但是現在 actionCreactor 這個檔案裡並沒有 store 這個資料倉庫,也就沒有 dispatch 這個方法,怎麼辦呢?

實際上,當我們建立一個內容是函式的 action 時,返回的函式就會自動接收到 store.dispatch 這個方法,所以只要在返回的函式裡呼叫 dispatch ,然後派發 action 就好了, store 判斷接收的 action 是一個物件,就會接收併發送給 reducer 進行資料更新操作

export const getTodoList = () => {
  return (dispatch) => {
    axios.get('/list.json').then((res) => {
      const data = res.data
      const action = initListAction(data)
      dispatch(action)
    })
  }
}

在 TodoList 元件中引用這個建立內容是函式的 action

componentDidMount() {

const action = getTodoList ();
store.dispatch(action);
}

有的小夥伴可能會有疑問,就一個ajax請求,放在 componentDidMount 會有影響嗎?

考慮到後期程式碼量的增加,如果把非同步函式放在元件的生命週期裡,這個生命週期函式會變得越來越複雜,元件就會變得越來越大

所以,還是應該把這種複雜的業務邏輯或者非同步函式拆分到一個地方進行管理,現在藉助 redux-thunk ,就可以放在 actionCreactor 裡邊集中管理,除此之外,在做自動化測試的時候,測試 actionCreactor 這個方法,也會比測元件的生命週期函式要簡單的多

到底什麼是 Redux 中介軟體

view 到 redux 的過程中會派發一個 action , action 通過 Store 的 dispatch 方法,會派發給 store , store接收到 action ,再連同之前的 state 一起傳給 reducer , reducer 返回一個新的資料給 store , store 就可以去改變自己的 state ,元件接收到新的 state 就可以重新渲染頁面了

redux的中介軟體在這個流程裡邊,指的是誰和誰之間呢?指的是 action 和 store 中間

action 通過 dispatch 方法被傳遞給 store ,那麼 action 和 store 之間是不是就是 dispatch 這個方法呢?實際上,我們說的中介軟體就是指的 dispatch 方法的一個封裝,或者是對 dispatch 方法的一個升級

最原始的 dispatch 方法,接收到物件 action 後會傳遞給 store ,這就是沒有中介軟體的情況

對 dispatch 方法做了一個升級後,也就是使用中介軟體時,再呼叫 dispatch 方法,如何給 dispatch 傳遞的仍然是個物件, dispatch 就會把這個物件傳給 store ,跟之前的方法沒有任何區別;但是假如傳的是個函式,就不會直接傳遞給 store 了,會讓這個函式先執行,然後執行完之後需要呼叫 store ,這個函式再去呼叫 store

dispatch方法會根據引數的不同,執行不同的事情,如果引數是物件,就直接傳給store,如果是函式,那就把函式執行結束

所以,redux的中介軟體原理很簡單,就是對 store 的 dispatch 方法做一個升級,既可以接收物件,又可以接收函數了,那是用什麼方法進行的升級的呢?就是用 redux-thunk 這個中介軟體進行升級的

當然,redux的中介軟體還有 redux-log ,原理就是在派發 action 給 store 之前先 console.log 出來;還有 redux-saga ,接下來需要講解的

React-Redux 的使用

目前我們已經瞭解了 react 和 redux ,那 React-Redux 是什麼呢?它是一個第三方的模組,可以在 react 中非常方便是使用 redux

重新來編寫 todolist 功能,在 index 檔案中引入 react-redux

import React from 'react'
import ReactDOM from 'react-dom'
import TodoList from './TodoList'
import { Provider } from 'react-redux'
import store from './store'

const App = (
  <Provider store={store}>
    <TodoList />
  </Provider>
)

ReactDOM.render(App, document.getElementById('root'))

Provider 實質是一個元件,是一個提供器,是 react-redux 的一個核心API,連線著 store , Provider 裡邊所有的元件,都有能力獲取到 store 裡邊的內容

react-redux 的另一個核心方法叫做 connect ,接收三個引數,最後一個引數是連線的元件,前面兩個是連線的規則

之前說 Provider 元件連線了 store , Provider 內部的元件有能力獲取到 store ,是怎樣獲取的呢?就是通過 connect 這個方法獲取到裡面的資料的

意思是讓 TodoList 元件和 store 進行連線,所以 connect 方法的意思是做連線,在做連線時需要有一定的方式和規則,就是用 mapStateToProps 方法來做關聯,翻譯為中文就是把 store 裡的資料 inputValue 對映到元件 inputValue 這個位置,為元件的 props 的資料

import React, { Component } from 'react'
import { connect } from 'react-redux'

class TodoList extends Component {
  render () {
    return (
      <div>
        <div>
          <input value={this.props.inputValue} />
          <button>提交</button>
        </div>
        <ul>
          <li>Dell</li>
        </ul>
      </div>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    inputValue: state.inputValue,

  }
}

export default connect(mapStateToProps, null)(TodoList)

如果需要對 store 的資料做修改,dispatch 是指的 store.dispatch ,可以通過 mapDispatchToProps 方法把 store.dispatch 掛載到props上,為什麼呢?

因為想要改變 store 裡的內容,就要呼叫 dispatch 方法, dispatch 方法被對映到了 props 上,所以就可以通過 this.props.dispatch 方法去呼叫了

import React, { Component } from 'react'
import { connect } from 'react-redux'

class TodoList extends Component {
  render () {
    return (
      <div>
        <div>
          <input value={this.props.inputValue} onChange={this.props.handleInputChange} />
          <button>提交</button>
        </div>
        <ul>
          <li>Dell</li>
        </ul>
      </div>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    inputValue: state.inputValue
  }
}
const mapDispatchToProps = (dispatch) => {
  return {
    handleInputChange(e) {
      const action = {
        type: 'change_input_value',
        value: e.target.value
      }
      dispatch(action)
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(TodoList)

現在在 input 裡輸入值的功能就完成了,那todolist的增加功能怎麼實現呢?

(TodoList.js)

<button onClick={this.props.handleClick}>提交</button>

const mapDispatchToProps = (dispatch) => {
  return {
    handleInputChange(e) {
      const action = {
        type: 'change_input_value',
        value: e.target.value
      }
      dispatch(action)
    },

    handleClick() {
      const action = {
        type: 'add_todo_item'
      }
      dispatch(action)
    }
  }
} 

(reducer.js)

export default (state = defaultState, action) => {
  if (action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state))
    newState.inputValue = action.value
    return newState
  }
  if (action.type === 'add_todo_item') {
    const newState = JSON.parse(JSON.stringify(state))
    newState.list.push(newState.inputValue)
    newState.inputValue = ''
    return newState
  }
  return state
}

點選這個 button 的時候,會執行 handleClick 這個方法,這個方法會把創建出來的 action 傳給 store ,再傳給 reducer, reducer 接收到這個 action 之後,去處理資料,把新的資料返回出去,新的資料就包含列表項的新內容了,資料發生了改變,todolist 元件恰好又通過 connect 跟資料做了連線,所以這塊是個自動的流程,資料一旦發生改變,這個元件自動就會跟的變

以前還需要 store.subscribe 做訂閱,現在連訂閱都可以不用了,頁面自動跟隨資料發生變化

這樣寫就實現了增加 item 的功能,

比如 item 的刪除操作, action 要通過 actionCreator 來建立,同時,還需要把 action 的 type 字串放在 actionType 裡面進行管理等等

建立 TodoList 這個元件,正常來說都是 export default TodoList ,把這個元件匯出出去,但是�現在 export defalut 出的東西是通過 connect 方法執行的結果,connect 方法做了一件什麼事呢?

它把這些對映關係和業務邏輯整合到了 TodoList 這個 UI 元件之中,所以 connect 方法可以這樣理解,TodoList 是一個 UI 元件,當你用 connect 把這個 UI 元件和一些資料和邏輯相結合時,返回的內容實際就是一個容器元件了,容器元件可以理解成資料處理包括派發這樣的業務邏輯,對 UI 元件進行包裝,去呼叫這些UI元件,資料和方法都準備好了

有的小夥伴可能在網上看到過這樣的描述,react-redux 元件既有 UI 元件,又有容器元件。UI 元件就是 TodoList 這個東西,而容器元件就是 connect 方法返回的結果,或者說 connect 方法執行生成的內容

所以 export default 匯出的內容就是 connect 方法執行的結果,是一個容器元件