關于React的入門知識

  1. 受控組件 V.S. 非受控組件

     <FInput value={x} onChange={fn}/> 受控組件:渲染表單的React組件 還控制著 用戶輸入表單時變化的操作,每次狀態變化都調用onChange,這種組件叫受控組件。表單數據是由 React 組件來管理的。
     <FInput defaultValue={x} ref={input}/> 非受控組件:React賦予組件一個初始值,但是不去控制后續的更新。表單數據將交由 DOM 節點來處理。
    
    
  1. React 有哪些生命周期函數?分別有什么用?(Ajax請求放在哪個階段?)
    初始化階段:
    componentWillMount()組件即將被渲染到頁面之前觸發,此時可以進行開啟定時器、向服務器發送請求等操作
    render()組件渲染
    componentDidMount()組件已經被渲染到頁面中后觸發:此時頁面中有了真正的DOM的元素,可以進行DOM相關的操作
    運行中階段:
    componentWillReceiveProps()組件接收到屬性時觸發
    shouldComponentUpdate()當組件接收到新屬性,或者組件的狀態發生改變時觸發。組件首次渲染時并不會觸發
    componentWillUpdate()組件即將被更新時觸發
    render()
    componentDidUpdate()組件被更新完成后觸發。頁面中產生了新的DOM的元素,可以進行DOM操作
    銷毀階段
    componentWillUnmount() 組件被銷毀時觸發。
    1. componentDidMount()中請求數據。
  2. React 如何實現組件間通信?
    1. 父子靠props 傳函數
    2. 爺孫可以穿兩次props
    3. 任意組件用 Redux(也可以自己寫一個 eventBus
  3. shouldComponentUpdate 有什么用?
    1. 用于在沒有必要更新 UI 的時候返回 false,以提高渲染性能。
    2. 但是不要濫用,這是React提供的一個緊急出口,必要的時候再使用,因為它的維護成本比較高,比如你新加了一個props,但是又忘記在shouldComponentUpdate 寫,就會引起bug。
    3. 如何優化?
  4. 虛擬 DOM 是什么?
    1. 要點:虛擬 DOM 就是用來模擬 DOM 的一個對象,這個對象擁有一些重要屬性,并且更新 UI 主要就是通過對比(DIFF)舊的虛擬 DOM 樹 和新的虛擬 DOM 樹的區別完成的。
    2. 參考:http://foio.github.io/virtual-dom/
  5. 什么是高階組件?
    1. 文檔原話——高階組件就是一個函數,且該函數接受一個組件作為參數,并返回一個新的組件。
    2. React-Redux 里 connect 就是一個高階組件,比如 connect(mapState)(MyComponent) 接受組件 MyComponent,返回一個具有狀態的新 MyComponent 組件。
  6. React diff 的原理是什么?
    首先,React diff遵循三個策略:1. Web UI 中,DOM節點跨層級的移動操作特別少,可以忽略不計。2.擁有相同類的兩個組件將生成相似的樹形結構,擁有不同類的兩個組件將生成不同的樹形結構。3. 對于同一層級的一組子節點,可以通過唯一id來區分它們?;谝陨先齻€策略,React分別對tree diff、component diff、element diff進行了算法優化。
  • tree diff

    React對樹的算法進行了優化,對樹進行分層比較,兩棵樹只比較同一層級的節點。DOM節點跨層級的操作少到忽略不計,針對這一點,React通過updateDepth對Virtual DOM樹進行層級控制,只會對相同顏色方塊內的DOM節點進行比較,當發現節點不存在,則會刪除該節點以及所有子節點,不會再進行進一步比較,所以只要對DOM樹進行一次遍歷,就能完成整個DOM樹的比較。
    tree diff.jpg
  • component diff
    如果是同一類型的組件,則按照原策略進行Virtual DOM Tree的比較。如果不是呢,則講組件判斷為dirty component,從而替換整個組件下的所有子節點。
    對于同一類型的組件,React允許用戶使用 shouldComponentUpdate()來判斷該組件是否需要diff。
    如下圖,當component D 改變為 component G時,即使兩個組件結構相似,一旦React判斷兩者為不同類型的組件,則不會進行比較,而是直接刪除component D,重新創建component G以及所有子節點。

    component diff.jpg

  • element diff
    當節點處于同一層級時,React diff提供了三種節點操作
    INSERT_MARKUP(插入):新的component類型不在老集合里,即是全新的節點,則需要對新節點執行插入操作。
    MOVE_EXISTING(移動):新的component類型在老集合里,且element是可更新的類型,generateComponentChildren 已調用receiveComponent,這種情況下prevChild=nextChild,就需要執行移動操作,可以復用以前的DOM節點。
    REMOVE_NODE(刪除):老component類型在新集合里面也有,但對應的element不同則不能直接進行復用和更新,需要執行刪除操作。老component類型在新集合里沒有,也要執行刪除操作。
    開發者對同一層級的子節點,可以添加唯一索引進行區分,這樣在diff時,涉及到只是位置變化的,可以只移動元素,避免刪除創建等重復的操作。

  1. Redux 是什么?
    1. ReduxJavaScript 狀態管理工具,提供可預測化的狀態管理。
    2. Action:是把數據從應用傳到 Store 的有效載荷。它是 Store 數據的唯一來源。改變 State 的唯一辦法,就是使用 Action。
import { ADD_TODO } from "../actionTypes";

export const addTodo = (payload:any) => {
    return {
        type: ADD_TODO,
        payload
    }
}

Reducers: 指定了應用狀態的變化如何響應 actions并發送到store 的,記住 actions只是描述了有事情發生了這一事實,并沒有描述應用如何更新 state
Reducer 是一個函數,它接受 Action 和當前State 作為參數,返回一個新的 State。
我們還可以將拆分后的Reducer 放到不同的文件中, 以保持其獨立性并用于專門處理不同的數據域。然后使用combineReducers將多個Reducer合并成一個,輸出成一個大的對象。

import { ADD_TODO } from "../actionTypes";

export default function (state = [],action:any) {
    switch (action.type){
        case ADD_TODO:
            return [...state,action.payload]
        default:
            return state
    }
}

import { ADD_TOMATO } from "../actionTypes";

export default function (state = [], action: any) {
    switch(action.type) {
        case ADD_TOMATO:
            return [...state, action.payload]
        default:
            return state
    }
}
//使用combineReducers
import { combineReducers } from "redux";
import todos from './todos'
import tomatoes from './tomatoes'

export default combineReducers({ todos, tomatoes });

Store: 就是保存數據的地方,你可以把它看成一個容器。整個應用只能有一個 Store。Redux提供createStore這個函數,接受reducers,用來生成 Store。

import { createStore } from "redux";
import rootReducer from "./reducers"; 

const store = createStore(rootReducer)

export default store

Store有以下職責:

  • 維持應用的 state;
  • 提供 getState() 方法返回應用當前的 state 樹。它與 store 的最后一個 reducer 返回值相同。
  • 提供 dispatch(action) 方法分發action。這是觸發 state 變化的惟一途徑。
  • 通過 subscribe(listener) 注冊監聽器;
  • 通過 subscribe(listener) 返回的函數注銷監聽器。

connect:該API連接React組件與 Redux store,連接操作不會改變原來的組件類。反而返回一個新的已與 Redux store 連接的組件類。connect可以接受參數,將參數注入到組件當中

const mapStateToProps = (state: { todos: any; }, ownProps: any) => ({//注入props
    ...ownProps
})

const mapDispatchToProps = {//注入屬性
    editTodo, 
    updateTodo
}

export default connect(mapStateToProps, mapDispatchToProps)(todoItem)

<Provider store> 使組件層級中的 connect()方法都能夠獲得 Redux store。正常情況下,你的根組件應該嵌套在 <Provider> 中才能使用 connect() 方法。

ReactDOM.render(
  <Provider store={store}>
    <MyRootComponent />
  </Provider>,
  rootEl
)
  1. connect 的原理是什么?
    react-redux 庫提供的一個 API,connect的作用是讓你把組件和store連接起來,產生一個新的組件(connect 是高階組件)。

1.首先connect之所以會成功,是因為Provider組件:
- 在原應用組件上包裹一層,使原來整個應用成為Provider的子組件
- 接收Reduxstore作為props,通過context對象傳遞給子孫組件上的connect

2.connect做了什么:它真正連接ReduxReact,它包在我們的容器組件的外一層,它接收上面Provider 提供的store里面的statedispatch,傳給一個構造函數,返回一個對象,以屬性形式傳給我們的容器組件。

3.主邏輯源碼

export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
  return function wrapWithConnect(WrappedComponent) {
    class Connect extends Component {
      constructor(props, context) {
        // 從祖先Component處獲得store
        this.store = props.store || context.store
        this.stateProps = computeStateProps(this.store, props)
        this.dispatchProps = computeDispatchProps(this.store, props)
        this.state = { storeState: null }
        // 對stateProps、dispatchProps、parentProps進行合并
        this.updateState()
      }
      shouldComponentUpdate(nextProps, nextState) {
        // 進行判斷,當數據發生改變時,Component重新渲染
        if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {
          this.updateState(nextProps)
            return true
          }
        }
        componentDidMount() {
          // 改變Component的state
          this.store.subscribe(() = {
            this.setState({
              storeState: this.store.getState()
            })
          })
        }
        render() {
          // 生成包裹組件Connect
          return (
            <WrappedComponent {...this.nextState} />
          )
        }
      }
      Connect.contextTypes = {
        store: storeShape
      }
      return Connect;
    }
  }

connect是一個高階函數,首先傳入mapStateToProps、mapDispatchToProps等參數,然后返回一個生產Component的函數(wrapWithConnect),然后再將真正的Component作為參數傳入wrapWithConnect,這樣就生產出一個經過包裹的Connect組件,該組件具有如下特點:

  • 通過props.store獲取祖先Componentstore
  • props包括statePropsdispatchPropsparentProps,合并在一起得到nextState,作為props傳給真正的Component
  • componentDidMount時,添加事件this.store.subscribe(this.handleChange),實現頁面交互
  • shouldComponentUpdate時判斷是否有避免進行渲染,提升頁面性能,并得到nextState
  • componentWillUnmount時移除注冊的事件this.handleChange
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • [toc] REACT react :1.用來構建用戶界面的 JAVASCRIPT 庫2.react 專注于視圖層...
    撥開云霧0521閱讀 1,478評論 0 1
  • 深入JSX date:20170412筆記原文其實JSX是React.createElement(componen...
    gaoer1938閱讀 8,104評論 2 35
  • React 中 keys 的作用是什么? Keys 是 React 用于追蹤哪些列表中元素被修改、被添加或者被移除...
    e2ee31170666閱讀 1,300評論 1 3
  • 今天的React題沒有太多的故事…… 半個月前出了248個Vue的知識點,受到很多朋友的關注,都強烈要求再出多些R...
    浪子神劍閱讀 10,121評論 6 106
  • 項目地址: 項目地地址參考地址: bilibili 1.火熱的0配置的打包工具parcel 地址: parcel官...
    第十人i閱讀 307評論 0 0