淺析 react

 

  1. JSX
    1. 註釋:在一個組件的子元素位置使用註釋要用 {} 包起來
      1. const App = ( 
         <Nav> 
         {/* 節點註釋 */} 
         <Person 
         /* 多行 
         註釋 */ 
         name={window.isLoggedIn ? window.name : ''} 
         /> 
         </Nav> 
        ); 

          但 HTML 中有一類特殊的註釋——條件註釋,它經常使用於判斷瀏覽器的版本:html

      2. <!--[if IE]> 
         <p>Work in IE browser</p> 
        <![endif]-->

          上述方法能夠經過使用 JavaScript 判斷瀏覽器版原本替代: react

      3. { 
         (!!window.ActiveXObject || 'ActiveXObject' in window) ? 
         <p>Work in IE browser</p> : '' 
        } 
    2. 元素屬性  算法

      1. class 屬性改成 className    瀏覽器

      2. for 屬性改成 htmlFor  緩存

  2. React 組件的構建less

    1. React 組件即爲組件元素
    2. React 組件基本上由 3 個部分組成——屬性(props)、狀態(state)以及生命週期方法
    3. React 組件能夠接收參數,也可能有自身狀態。一旦接收到的參數或自身狀態有所改變,React 組件就會執行相應的生命週期方法,最後渲染。整個過程徹底符合傳統組件所定義的組件職責。
    4. React 組件的構建方法
      1. React.createClassdom

          1. const Button = React.createClass({ 
             getDefaultProps() { 
             return { 
             color: 'blue', 
             text: 'Confirm', 
             }; 
             }, 
             
             render() { 
             const { color, text } = this.props; 
             
             return ( 
             <button className={`btn btn-${color}`}> 
             <em>{text}</em> 
             </button> 
             ); 
             } 
            });
            1. 在 0.14 版本發佈以前,這一直都是 React 官方惟一指定的組件寫法
            2. 只用寫成 <Button />,就能夠被解析成 React.createElement(Button) 方法來建立 Button
              實例,這意味着在一個應用中調用幾回 Button,就會建立幾回 Button 實例異步

      2. ES6 classes函數

          1. import React, { Component } from 'react'; 
             
            class Button extends Component { 
             constructor(props) { 
             super(props); 
             } 
             
             static defaultProps = { 
             color: 'blue', 
             text: 'Confirm', 
             }; 
             
             render() { 
             const { color, text } = this.props; 
             
             return ( 
             <button className={`btn btn-${color}`}> 
             <em>{text}</em> 
             </button> 
             ); 
             } 
            }

            React 的全部組件都繼承自頂層類 React.Component。它的定義很是簡潔,只是初始化了
            React.Component 方法,聲明瞭 props、context、refs 等,並在原型上定義了 setState 和
            forceUpdate 方法。內部初始化的生命週期方法與 createClass 方式使用的是同一個方法
            建立的。優化

      3. 無狀態函數(stateless function)

        1. function Button({ color = 'blue', text = 'Confirm' }) { 
           return ( 
           <button className={`btn btn-${color}`}> 
           <em>{text}</em> 
           </button> 
           ); 
          } 
          1. 無狀態組件只傳入 props 和 context 兩個參數;也就是說,它不存在 state,也沒有生命周
            期方法,組件自己即上面兩種 React 組件構建方法中的 render 方法。不過,像 propTypes 和
            defaultProps 仍是能夠經過向方法設置靜態屬性來實現的。

          2. 在適合的狀況下,咱們都應該且必須使用無狀態組件。無狀態組件不像上述兩種方法在調用
            時會建立新實例,它建立時始終保持了一個實例,避免了沒必要要的檢查和內存分配,作到了內部
            優化。

    5. React 數據流

      1. 在 React 中,數據是自頂向下單向流動的,即從父組件到子組件
      2. state 與 props 是 React 組件中最重要的概念
      3. 若是頂層組件初始化 props,那麼 React 會向下遍歷整棵組件樹,從新嘗試渲染全部相關的子組件。

      4. 而 state 只關心每一個組件本身內部的狀態,這些狀態只能在組件內改變。

      5. 把組件當作一個函數,那麼它接受了 props 做爲參數,內部由 state 做爲函數的內部參數,返回一個 Virtual DOM 的實現

    6. state: setState 
      1. 是異步操做函數;
      2. 組件在尚未渲染以前, this.setState 尚未被調用;
      3. 批量執行 State 轉變時讓 DOM 渲染更快(相對比一個一個的setState的來的快)。
    7. props 自己是不可變的:組件的 props 必定來自於默認屬性或經過父組件傳遞而來
      1. 若是說要渲染一個對 props 加工後的值,最簡單的方法就是使用局部變量或直接在 JSX 中計算結果

      2. React 爲 props 一樣提供了默認配置,經過 defaultProps 靜態變量的方式來定義
      3. React.Children 是 React 官方提供的一系列操做 children 的方法。它提供諸如 map、forEach、count 等實用函數,能夠爲咱們處理子組件提供便利

    8. 組件 props

      1. 如今咱們發現對於 state 來講,它的通訊集中在組件內部;對於 props 來講,它的通訊是父組

        件向子組件的傳播。

    9. propTypes:用於規範 props 的類型與必需的狀態
    10. React 生命週期

      1. 掛載、渲染和卸載這
      2. import React, { Component, PropTypes } from 'react'; 
         
        class App extends Component { 
         static propTypes = { 
         // ... 
         }; 
         
         static defaultProps = { 
         // ... 
         }; 
         
         constructor(props) { 
         super(props); 
         
         this.state = { 
         // ... 
         }; 
         } 
         
         componentWillMount() { 
         // ... 
         } 
        componentDidMount() { 
         // ... 
         } 
         
         render() { 
         return <div>This is a demo.</div>; 
         } 
        } 
      3. componentWillMount 方法會在 render 方法以前執行,而 componentDidMount 方法會在 render 方法以後執行,這些都只會在組件初始化時運行一次

      4. 組件卸載很是簡單,只有 componentWillUnmount 這一個卸載前狀態
      5. import React, { Component, PropTypes } from 'react'; 
         
        class App extends Component { 
         componentWillUnmount() { 
         // ... 
         } 
         
         render() { 
         return <div>This is a demo.</div>; 
         }

        在 componentWillUnmount 方法中,咱們經常會執行一些清理方法,如事件回收或是清除定
        時器。

    11. 數據更新過程
      1. 更新過程指的是父組件向下傳遞 props 或組件自身執行 setState 方法時發生的一系列更新動做

      2. import React, { Component, PropTypes } from 'react'; 
         
        class App extends Component { 
         componentWillReceiveProps(nextProps) { 
         // this.setState({}) 
         } 
         
         shouldComponentUpdate(nextProps, nextState) { 
         // return true; 
         } 
         
         componentWillUpdate(nextProps, nextState) { 
         // ... 
         } 
         
         componentDidUpdate(prevProps, prevState) { 
         // ... 
         } 
         
         render() { 
         return <div>This is a demo.</div>; 
         } 
        } 

        若是組件自身的 state 更新了,那麼會依次執行 shouldComponentUpdate、componentWillUpdate 、
        render 和 componentDidUpdate。

      3. 須要注意的是,你不能在 componentWillUpdate 中執行 setState
      4. 若是組件是由父組件更新 props 而更新的,那麼在 shouldComponentUpdate 以前會先執行 

        componentWillReceiveProps 方法。此方法能夠做爲 React 在 props 傳入後,渲染以前 setState 的
        機會。在此方法中調用 setState 是不會二次渲染的。

  3.  React 與 DOM

    1.  ReactDOM
      1. findDOMNode

        1. DOM 真正被添加到 HTML 中的生命週期方法是 componentDidMount 和 componentDidUpdate 方法

        2. 假設要在當前組件加載完時獲取當前 DOM,則可使用 findDOMNode:

          import React, { Component } from 'react'; 
          import ReactDOM from 'react-dom'; 
           
          class App extends Component { 
           componentDidMount() { 
           // this 爲當前組件的實例 
           const dom = ReactDOM.findDOMNode(this); 
           } 
           
           render() {} 
          } 
        3. findDOMNode 只對已經掛載的組件有效。

      2. render

        1. 爲何說只有在頂層組件咱們纔不得不使用 ReactDOM 呢?這是由於要把 React 渲染的 Virtual DOM 渲染到瀏覽器的 DOM 當中,就要使用 render 方法了:
          1.  
            ReactComponent render( 
             ReactElement element, 
             DOMElement container, 
             [function callback] 
            ) 

             

          2. 該方法把元素掛載到 container 中,而且返回 element 的實例(即 refs 引用)。固然,若是是無狀態組件,render 會返回 null。當組件裝載完畢時,callback 就會被調用。

          3. 當組件在初次渲染以後再次更新時,React 不會把整個組件從新渲染一次,而會用它高效的 DOM diff 算法作局部的更新。這也是 React 最大的亮點之一!

      3. refs

  4. 事件系統
    1. 在 JSX 中,咱們必須使用駝峯的形式來書寫事件的屬性名(好比onClick),而 HTML 事件則須要使用所有小寫的屬性名(好比 onclick)。

    2. HTML 的屬性值只能是 JavaScript 代碼字符串,而在 JSX 中,props 的值則能夠是任意類型,這裏是一個函數指針。

    3. 在 React 底層,主要對合成事件作了兩件事:事件委ี和自動綁定。

      1. 事件委派
        1. React並不會把事件處理函數直接綁定到真實的節點上,而是把全部事件綁定到結構的最外層,使用一個統一的事件監聽器,這個事件監聽器上維持了一個映射來保存全部組件內部的事件ᄢ聽和處理函數。
        2. 當組件掛載或卸載時,只是在這個統一的事件ᄢ聽器上插入或刪除一些對象;當事件發生時,首先被這個統一的事件ᄢ聽器處理,而後在映射裏找到真正的事件處理函數並調用。這樣作簡化了事件處理和回收機制,效率也有很大提高。

      2. 自動綁定

        1. 在 React 組件中,每一個方法的上下文都會指向該組件的實例,即自動綁定 this 爲當前組件
        2. 並且 React 還會對這種引用進行緩存,以達到 CPU 和內存的最優化
        3. 在使用 ES6 classes 或者純函數時,這種自動綁定就不復存在了,咱們須要手動實現 this 的綁定。

          1. bind 方法

            1. import React, { Component } from 'react'; 
                 
              class App extends Component { 
               handleClick(e, arg) { 
               console.log(e, arg); 
               } 
               
               render() { 
               // 經過bind方法實現,能夠傳遞參數 
               return <button onClick={this.handleClick.bind(this, 'test')}>Test</button>; 
               } 
              }
          2. 若是方法只綁定,不傳參,那 stage 0 ᕘ案中提供了一個便૸的方案①——Ԥ冒號語法,其做用與 this.handleClick.bind(this) 一致,而且 Babel 已經實現了該提案。

            1. import React, { Component } from 'react';
              class App extends Component { 
               handleClick(e) { 
               console.log(e); 
               } 
               
               render() { 
               return <button onClick={::this.handleClick}>Test</button>; 
               } 
              }
          3. 構造器內聲明
            1. 在組件的構造器內完成了 this 的綁定,這種綁定方式的好處在於僅須要進行一次綁定,而不須要每次調用事件ᄢ聽器時去執行綁定操做:
              1. import React, { Component } from 'react'; 
                 
                class App extends Component { 
                 constructor(props) { 
                 super(props); 
                 
                 this.handleClick = this.handleClick.bind(this); 
                 } 
                 
                 handleClick(e) { 
                 console.log(e); 
                 } 
                 
                 render() { 
                 return <button onClick={this.handleClick}>Test</button>; 
                 } 
                } 
          4. 箭頭函數
            1. 箭頭函數不只是函數的「語法糖」,它還自動綁定了定義此函數做用域的 this,所以咱們不須要再對它使用 bind 方法。

              1. import React, { Component } from 'react'; 
                 
                class App extends Component { 
                 const handleClick = (e) => { 
                   console.log(e); 
                 }; 
                 
                 render() { 
                 return <button onClick={this.handleClick}>Test</button>; 
                 } 
                } 
                或 
                import React, { Component } from 'react'; 
                 
                class App extends Component { 
                 handleClick(e) { 
                 console.log(e); 
                 } 
                 
                 render() { 
                return <button onClick={() => this.handleClick()}>Test</button> 
                 } 
                }
          5. 在 React中使用原生事件

            1. componentDidMount 會在組件已經完成安裝而且在瀏覽器中存在真實的 DOM 後調用,此時咱們就能夠完成原生事件的綁定。

            2. 值得注意的是,在 React 中使用 DOM 原生事件時,必定要在組件卸載時手動移除,不然極可能出現內存泄漏的問題。而使用合成事件系統時則不須要,由於 React 內部已經幫你妥ؒ地處理了。

          6. 合成事件與原生事件混用

            1. 不要將合成事件與原生事件混用,如:
              1. componentDidMount() { 
                   document.body.addEventListener('click', e => { 
                        this.setState({ 
                            active: false, 
                         }); 
                    }); 
                 
                    document.querySelector('.code').addEventListener('click', e => { 
                       e.stopPropagation(); 
                     }) 
                } 
                 
                componentWillUnmount() { 
                 document.body.removeEventListener('click'); 
                 document.querySelector('.code').removeEventListener('click'); 
                }
            2. 經過 e.target判斷來避免

              1. componentDidMount() { 
                 document.body.addEventListener('click', e => { 
                 if (e.target && e.target.matches('div.code')) { 
                 return; 
                 } 
                 
                 this.setState({ 
                 active: false, 
                 }); 
                 }); 
                }
          7. 對比React合成事件與JavaScript原生事件

            1. 事件傳播與阻止事件傳播

      3. 非受控組件
        1. 若是一個表單組件沒有value props(單選按鈕和複選框對應的是checked prop)時,就能夠稱爲非受控組件。
        2. 非受控組件是一種反模式,它的值不受組件自身的state或props控制。一般要經過爲其添加ref prop來訪問渲染後的底層DOM元素
      4. 對比受控組件和非受控組件
        1. 受控組件
          1. <input   value={this.state.value}  
                 onChange={e => {     
                    this.setState({ value:           e.target.value.toUpperCase() 
                })   
                }} />
        2. 非受控組件

          1. <input   
                defaultValue={this.state.value}   
                onChange={e => {     
                this.setState({ value:     e.target.value.toUpperCase() })   }} />

              

        3. 若是不對受控組件綁定change事件,咱們在文本框中輸入任何值都不會起做用。多數狀況下,對於非受控組件,咱們並不須要提供change事件。
        4. 最大的區別:非受控組件的狀態並不會受應用狀態的控制,應用中也多了局部組件狀態,而受控組件的值來自於組件的state.
    4. mixin:
      1. import React, { Component } from 'React'; 
        import { mixin } from 'core-decorators';
        const PureRender = { shouldComponentUpdate() {} }; const Theme = { setTheme() {} }; @mixin(PureRender, Theme) class MyComponent extends Component { render() {} }
      2. 這個mixin與createClass中的mixin的區別:
        1. 以前直接給對象的prototype屬性賦值,但這裏用了getOwnPropertyDescriptor 和defineProperty 這兩個方法
        2. 這樣實現的好處在於 defineProperty 這個方法,也就是定義與複製的區別,定義是對已有的定義,賦值則是覆蓋已有的定義。
  5. 初探React 生命週期  
    1. 當首次掛載組件時,按順序執行 getDefaultProps、getInitialState、componentWillMount、 render 和 componentDidMount。
    2. 當卸載組件時,執行 componentWillUnmount。
    3. 當從新掛載組件時,此時按順序執行 getInitialState、componentWillMount、render 和 componentDidMount,但並不執行 getDefaultProps。
    4. 當再次渲染組件時,組件接受到更新狀態,此時按順序執行 componentWillReceiveProps、 shouldComponentUpdate、componentWillUpdate、render 和 componentDidUpdate。 
  6. 生命週期原理
    1.   
    2. 經過 mountComponent 掛載組件,初始化序號、標記等參數,判斷是否爲無狀態組件,並進行對應的組件初始化工做,好比初始化 props、context 等參數。利用 getInitialState 獲取初始化state、初始化更新對列和更新狀態。
    3. 若存在 componentWillMount,則執行。若是此時在 componentWillMount 中調用 setState 方法,是不會觸發 re-render的,而是會進行 state 合併,且 inst.state = this._processPendingState
      (inst.props, inst.context) 是在 componentWillMount 以後執行的,所以 componentWillMount 中的 this.state 並非最新的,在 render 中才能夠獲取更新後的 this.state。
    4. mountComponent 本質上是經過遞歸渲染內容的,因爲遞歸的特性,父組件的componentWillMount 在其子組件的 componentWillMount 以前調用,而父組件的 componentDidMount在其子組件的 componentDidMount 以後調用。

    5. 禁止在 shouldComponentUpdate 和 componentWillUpdate 中調用 setState,這會形成循環調用,直至耗光瀏覽器內存後崩潰。
    6. 若是存在 componentWillUnmount,則執行並重置全部相關參數、更新隊列以及更新狀態,若是此時在 componentWillUnmount 中調用 setState,是不會觸發 re-render 的,這是由於全部更新隊列和更新狀態都被重置爲 null,並清除了公共類,完成了組件卸載操做。

  7. React diff 算法的 3 個策略
    1.  策略一: Web UI DOM 節點跨層級的移動操做特別少,能夠忽略不計
    2.  策略二:擁有相同類的兩個組件將會生成類似的樹形結構,擁有不一樣類的兩個組件將會
      生成不一樣的樹形結構。 
    3.    策略三:對於同一層級的一組子節點,它們能夠經過惟一 id 進行區分。
  8.  基於以上策略, React 分別對 tree diffcomponent diff 以及 element diff 進行算法優化。
    1.  tree diff
      1.  基於策略一, React 對樹的算法進行了簡潔明瞭的優化,即對樹進行分層比較,兩棵樹只會
        對同一層次的節點進行比較
      2. 既然 DOM 節點跨層級的移動操做少到能夠忽略不計,針對這一現象,React 經過 updateDepth
        Virtual DOM 樹進行層級控制,只會對相同層級的 DOM 節點進行比較,即同一個父節點下的
        全部子節點。當發現節點已經不存在時,則該節點及其子節點會被徹底刪除掉,不會用於進一步
        的比較。這樣只須要對樹進行一次遍歷,便能完成整個 DOM 樹的比較。
    2.  component diff
      1. 若是是同一類型的組件,按照原策略繼續比較 Virtual DOM 樹便可。
      2. 若是不是,則將該組件判斷爲 dirty component,從而替換整個組件下的全部子節點。
      3. 對於同一類型的組件,有可能其 Virtual DOM 沒有任何變化,若是可以確切知道這點,那
        麼就能夠節省大量的 diff 運算時間。所以, React 允許用戶經過 shouldComponentUpdate()
        來判斷該組件是否須要進行 diff 算法分析。
    3.  element diff
      1.  當節點處於同一層級時, diff 提供了 3 種節點操做,分別爲 INSERT_MARKUP(插入)、 MOVE_
        EXISTING(移動)和 REMOVE_NODE(刪除)。
        1.    INSERT_MARKUP: 新的組件類型不在舊集合裏,即全新的節點,須要對新節點執行插入操做。
        2.   MOVE_EXISTING:舊集合中有新組件類型,且 element 是可更新的類型, generateComponentChildren 已調用 receiveComponent,這種狀況下 prevChild=nextChild,就須要作移動操做,能夠複用之前的 DOM 節點。
        3.   REMOVE_NODE:舊組件類型,在新集合裏也有,但對應的 element 不一樣則不能直接複用和更
          新,須要執行刪除操做,或者ே組件不在新集合裏的,也須要執行刪除操做。
  9.  React Virtual DOM 樹轉換成 actual DOM 樹的最少操做的過程稱爲調和
相關文章
相關標籤/搜索