Entry to React

原教程內(nèi)容詳見(jiàn)精益 React 學(xué)習(xí)指南,這只是我在學(xué)習(xí)過(guò)程中的一些閱讀筆記,個(gè)人覺(jué)得該教程講解深入淺出,比目前大多數(shù)教程專業(yè)、系統(tǒng)。

1.1 Introduction to React

1.1.1 What is React?

React IS A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES , INCLUDING

  1. React.js
  2. ReactRenders: ReactDOM / ReactServer / ReactCanvas
  3. Flux模式及其實(shí)現(xiàn)
  4. React Components
  5. React Native
  6. GraphQI + Relay

1.1.2 Basic concept in React

  1. React.js
    React.js 是 React 的核心庫(kù),在應(yīng)用中必須先加載核心庫(kù)
    <script src="http://facebook.github.io/react/js/react.js"</script>
    <script src="http://facebook.github.io/react/js/react-dom.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  1. ReactDOM.js
    DOM渲染器,為了在 web 頁(yè)面中顯示開發(fā)的組件,需要調(diào)用 ReactDOM.render 方法, 第一個(gè)參數(shù)是 React 組件,第二個(gè)參數(shù)為 HTMLElement。
  2. JSX
    JSX 是 React 自定義的語(yǔ)法,最終 JSX 會(huì)轉(zhuǎn)化為 JS 運(yùn)行于頁(yè)面當(dāng)中
  3. Components
    Component是 React 中的核心概念,頁(yè)面當(dāng)中的所有元素都是通過(guò) React 組件來(lái)表達(dá)。
  4. Virtual DOM
    React 抽象出來(lái)的虛擬 DOM 樹,虛擬樹是 React 高性能的關(guān)鍵。
  5. one-way reactive data flow
    React 應(yīng)用的核心設(shè)計(jì)模式,數(shù)據(jù)流向自頂向下

1.1.3 Features of React

  1. Component的組合模式
    組合模式有時(shí)候又叫做部分-整體模式,它使我們樹型結(jié)構(gòu)的問(wèn)題中,模糊了簡(jiǎn)單元素和復(fù)雜元素的概念,客戶程序可以向處理簡(jiǎn)單元素一樣來(lái)處理復(fù)雜元素,從而使得客戶程序與復(fù)雜元素的內(nèi)部結(jié)構(gòu)解耦。
    React 就是基于組合模式, 無(wú)論是應(yīng)用等級(jí)還是一個(gè)表單亦或是一個(gè)按鈕都視為一個(gè)組件, 然后基于組件的組合構(gòu)建整個(gè)應(yīng)用,這樣的結(jié)構(gòu)一直是前端界想要卻遲遲不來(lái)的 web component。
    基于組合模式的優(yōu)點(diǎn):
  • 構(gòu)建可重用的組件:組件的開發(fā)能夠形成公司的組件庫(kù),每個(gè)業(yè)務(wù)的開發(fā)都能積累可重用的組件。
  • 無(wú)學(xué)習(xí)障礙:天然符合 HTML 的結(jié)構(gòu), 對(duì)前端開發(fā)者來(lái)說(shuō)幾乎沒(méi)有學(xué)習(xí)障礙。
  • 具有彈性的架構(gòu):組合模式很簡(jiǎn)單卻很有效,能夠構(gòu)建簡(jiǎn)單的頁(yè)面也能構(gòu)建大型的前端應(yīng)用。
  • 源碼高可維護(hù)性:開發(fā)只是工作中的一部分,應(yīng)用的上線才是噩夢(mèng)的開始,很多大型應(yīng)用因?yàn)閺?fù)制的業(yè)務(wù)邏輯導(dǎo)致無(wú)法快速響應(yīng)業(yè)務(wù)需求,可維護(hù)性低。
  1. One way data flow
    Javascript 是腳本語(yǔ)言,不能像靜態(tài)語(yǔ)言一樣通過(guò)編譯定位為題,想要清晰的定位到應(yīng)用中的 bug 需要深入了解業(yè)務(wù)代碼,對(duì)于大型前端應(yīng)用來(lái)說(shuō),因?yàn)闃I(yè)務(wù)代碼量很大且復(fù)雜,很難定位到 bug。 然而 React 的單向數(shù)據(jù)流的設(shè)計(jì)讓前端 bug 定位變得簡(jiǎn)單, 頁(yè)面的 UI 和數(shù)據(jù)的對(duì)應(yīng)是唯一的,我們可以通過(guò)定位數(shù)據(jù)變化就可以定位頁(yè)面展現(xiàn)問(wèn)題。
  2. High efficiency
    基于VirtualDOM算法可以讓只有需要改變的元素才去重渲染
  3. Separate frame design
    React.js 現(xiàn)在的版本已經(jīng)將源碼分開為 ReactDOM 和 React.js . 這就意味著 React 不僅僅能夠在 web 端工作, 甚至可以在服務(wù)端(nodejs),Native 端運(yùn)行。
    與此同時(shí), 我們可以自定義自己的渲染器, 實(shí)現(xiàn)比如 Three.js, Pixi.js, D3.js 的 React 方式渲染。

1.1.4 Where can React apply?

  1. web 端
  2. 原生應(yīng)用
    IOS、Android、Native 應(yīng)用
    這要?dú)w功于 facebook 開源的 React Native。 基于 React Native , 我們將可以使用 jsx 來(lái)實(shí)現(xiàn)具有原生應(yīng)用性能的 UI 運(yùn)行于 IOS 和 android 中,同時(shí)我們也可以通過(guò) NW.js 或者 Electron 來(lái)實(shí)現(xiàn)基于 React 的桌面應(yīng)用。
  3. 服務(wù)器端渲染
    React 除了在 Web 和 Native 環(huán)境以外, 也可以通過(guò) React 實(shí)現(xiàn)在服務(wù)器端渲染出 HTML。

1.2 JSX Grammar

1.2.1 Prepare running environment

  1. Babel REPL
    Babel REPL
    直接在 REPL 中寫 JSX 語(yǔ)法,可以實(shí)時(shí)的查看編譯后的結(jié)果。
  2. JSFiddle
    在線模式 React Fiddle
  3. Local development

1.2.2 JSX Grammar

創(chuàng)建 JSX 語(yǔ)法的本質(zhì)目的是為了使用基于 xml 的方式表達(dá)組件的嵌套,保持和 HTML 一致的結(jié)構(gòu),語(yǔ)法上除了在描述組件上比較特別以外,其它和普通的 Javascript 沒(méi)有區(qū)別。 并且最終所有的 JSX 都會(huì)編譯為原生 Javascript。

  1. jsx componments
    JSX 組件分為 HTML 組件和 React 組件
    HTML 組件就是 HTML 中的原生標(biāo)簽, 如:
function render() {
       return  <p> hello, React World </p>
}
function render() {
       return <ul> 
                 <li>list item 1</li>
                 <li>list item 2</li>
              </ul>
}

React 組件就是自定義的組件,如

// 定義一個(gè)自定義組件
var CustomComponnet = React.createClass({
        render: function() {
            return <div> custom component </div>
  }
});
// 使用自定義組件
function render() {
        return <p> <CustomComponent/> </p>
}
  1. properties of jsx components
    和 html 一樣,JSX 中組件也有屬性,傳遞屬性的方式也相同
    對(duì)于 HTML 組件
 function render() {
      return  <p title="title" >hello, React, world </p>
 }

如果是 React 組件可以定義自定義屬性,傳遞自定義屬性的方式:

  function render() {
      return <p> <CustomComponent customProps="data"/> </p>
      }
}

屬性即可以是字符串,也可以是任意的 Javascript 變量, 傳遞方式是將變量用花括號(hào), eg:

  function render() {
      var data = {a: 1, b:2};
      return <p> <CustomComponent customProps={data}/> </p>
  }

需要注意的地方上,屬性的寫法上和 HTML 存在區(qū)別,在寫 JSX 的時(shí)候,所有的屬性都是駝峰式的寫法,主要是出于標(biāo)準(zhǔn)的原因,駝峰式是 Javascript 的標(biāo)準(zhǔn)寫法,并且 React 底層是將屬性直接對(duì)應(yīng)到原生 DOM 的屬性,而原生 DOM 的屬性是駝峰式的寫法,這里也可以理解為什么類名不是 class 而是 className 了, 又因?yàn)?class 和 for 還是 js 關(guān)鍵字,所以 jsx 中:

class => className
for => htmlFor

除此之外比較特殊的地方是 data-*aria-*兩類屬性是和 HTML 一致的。

  1. curly braces
  • 顯示文本
  function render() {
        var text = "Hello, World"
        return <p> {text} </p>
  }
  • 運(yùn)算
funtion render() {
      var text = text;
      var isTrue = false;
      var arr = [1, 2, 3];
      return <p>
        {text}
        {isTrue ? "true" : "false"}
        {arr.map(function(it) {
           return <span> {it} </span>
        })}
        </p>
  }
  1. 限制規(guī)則
    render 方法返回的組件必須是有且只有一個(gè)根組件,錯(cuò)誤情況的例子
  // 無(wú)法編譯通過(guò),JSX 會(huì)提示編譯錯(cuò)誤
  function render() {
    return (<p> .... </p>
           <p> .... </p>)
  }
  1. 組件命名空間
    JSX 可以通過(guò)命名空間的方式使用組件, 通過(guò)命名空間的方式可以解決相同名稱不同用途組件沖突的問(wèn)題。如:
  function render() {
    return <p>
           <CustomComponent1.SubElement/>
           <CustomComponent2.SubElement/>
           </p>
  }

1.2.3 Understand JSX

  1. JSX 的編譯方式
    • 在 HTML 中引入 babel 編譯器, 如上 Hello World 程序中一樣。
    • 離線編譯 JSX,通過(guò) babel 編譯 JSX,細(xì)節(jié)我們將在第二章中講解。
  2. JSX 到 JS 的轉(zhuǎn)化
    Hello World 程序轉(zhuǎn)化為 JS 的代碼如下:
  var Hello = React.createClass({
       displayName: 'Hello',
       render: function() {
          return React.createElement("div", null, "Hello ", this.props.name);
     }
  });
  ReactDOM.render(
       React.createElement(Hello, {name: "World"}),
       document.getElementById('container')
  );

1.3 React Components

1.3.1 Create a component

  • 創(chuàng)建一個(gè) React 組件的方法為,調(diào)用 React.createClass 方法,傳入的參數(shù)為一個(gè)對(duì)象,對(duì)象必須定義一個(gè) render 方法,render 方法返回值為組件的渲染結(jié)構(gòu),也可以理解為一個(gè)組件實(shí)例(React.createElement 工廠方法的返回值),返回值有且只能為一個(gè)組件實(shí)例,或者返回 null/false,當(dāng)返回值為 null/false 的時(shí)候,React 內(nèi)部通過(guò) <noscript/> 標(biāo)簽替換
var MyComponent = React.createClass({
        render: function() {
            return <p>....</p>;
        }
});
  • Component命名空間
    可以看出 React.createClass 生成的組件類為一個(gè) Javascript 對(duì)象。 當(dāng)我們想設(shè)置命名空間組件時(shí),可以在組件下面添加子組件
 MyComponent.SubComponent = React.createClass({...});
 MyComponent.SubComponent.Sub = React.createClass({....});
  • 無(wú)狀態(tài)組件
    除了可以通過(guò) React.createClass 來(lái)創(chuàng)建組件以外,組件也可以通過(guò)一個(gè)普通的函數(shù)定義,函數(shù)的返回值為組件渲染的結(jié)果
function StatelessComponent(props) {
        return  <div> Hello {props.name} </div>
}

無(wú)狀態(tài)組件能夠優(yōu)化性能,因?yàn)槠鋬?nèi)部不會(huì)維護(hù)狀態(tài),React 內(nèi)部不會(huì)有一個(gè)對(duì)應(yīng)的組件實(shí)例,并且也沒(méi)有生命周期 hook

1.3.2 將組件渲染到 DOM 中

當(dāng)創(chuàng)建好了組件過(guò)后,為了將組件渲染到 DOM 中顯示出來(lái),需要兩個(gè)步驟:

  1. 在 HTML 中定義一個(gè)元素,設(shè)置 id 屬性
  2. JSX 中調(diào)用 ReactDOM.render 方法, 第一個(gè)參數(shù)為 組件,第二個(gè)為剛才定義的 DOM 元素
<!-- 定義的 DOM 元素 -->
<div id="example"></div>
<script type="text/babel">
    // 自定義組件 
    var MyComponent = React.createClass({
        render: function() {
            return <p>....</p>;
        }
    });
    // 調(diào)用 render 方法
    ReactDOM.render(
    <MyComponent/>,
    document.getElementById('example')
    );
</script>

1.3.3 States of components

React 的渲染結(jié)果是由組件屬性和狀態(tài)共同決定的,狀態(tài)和屬性的區(qū)別是,狀態(tài)維護(hù)在組件內(nèi)部,屬性是由外部控制,我們先介紹組件狀態(tài)相關(guān)細(xì)節(jié):
控制狀態(tài)的 API 為:

  • this.state:組件的當(dāng)前狀態(tài)
  • getInitialState:獲取組件的初始狀態(tài),在組件加載的時(shí)候會(huì)被調(diào)用一次,返回值賦予 this.state 作為初始值
  • this.setState:組件狀態(tài)改變時(shí),可以通過(guò) this.setState 修改狀
    • setState 方法支持按需修改,如 state 有兩個(gè)字段,僅當(dāng) setState 傳入的對(duì)象包含字段 key 才會(huì)修改屬性
  • 每次調(diào)用 setState 會(huì)導(dǎo)致重渲染調(diào)用 render 方法
  • 直接修改 state 不會(huì)重渲染組件
 var Switch = React.createClass({
        // 定義組件的初始狀態(tài),初始為關(guān)
        getInitialState: function() {
            return {
                open: false
            }
        },
        // 通過(guò) this.state 獲取當(dāng)前狀態(tài)
        render: function() {
            console.log('render switch component');
            var open = this.state.open;
            return <label className="switch"> 
                        <input type="checkbox" checked={open}/> 
                    </label>
        },
        // 通過(guò) setState 修改組件狀態(tài)
        // setState 過(guò)后會(huì) React 會(huì)調(diào)用 render 方法重渲染
        toggleSwitch: function() {
            var open = this.state.open;
            this.setState({
                open: !open
            });
        }
    })

1.3.4 Properties of components

前面已經(jīng)提到過(guò) React 組件可以傳遞屬性給組件,傳遞方法和 HTML 中無(wú)異, 可以通過(guò) this.props 獲取組件屬性
屬性相關(guān)的 API 為:

  • this.props: 獲取屬性值
  • getDefaultProps: 獲取默認(rèn)屬性對(duì)象,會(huì)被調(diào)用一次,當(dāng)組件類創(chuàng)建的時(shí)候就會(huì)被調(diào)用,返回值會(huì)被緩存起來(lái),當(dāng)組件被實(shí)例化過(guò)后如果傳入的屬性沒(méi)有值,會(huì)返回默認(rèn)屬性值
  • this.props.children:子節(jié)點(diǎn)屬性
  • propTypes: 屬性類型檢查
// props.name 表示代辦事項(xiàng)的名稱
    var TodoItem = React.createClass({
        render: function() {
            var props = this.props;
            return <div className="todo-item">
                        <span className="todo-item__name">{props.name}</span>
                    </div>
        }
    });
    ReactDOM.render(
        <TodoItem name="代辦事項(xiàng)1"/>, 
         document.getElementById('example'));
  1. children屬性
    組件屬性中會(huì)有一個(gè)特殊屬性 children ,表示子組件, 還是以上面一個(gè)組件為例子,我們可以換一種方式定義 name:
var TodoItem = React.createClass({
        render: function() {
            var props = this.props;
            return <div className="todo-item">
                        <span class="todo-item__name">{props.children}</span>
                    </div>
        }
    });
    ReactDOM.render(
        <TodoItem>代辦事項(xiàng)1</TodoItem>, 
         document.getElementById('example')); 
  1. 屬性類型檢查
    為了保證組件傳遞屬性的正確性, 我們可以通過(guò)定義 propsType 對(duì)象來(lái)實(shí)現(xiàn)對(duì)組件屬性的嚴(yán)格校驗(yàn)
var MyComponent = React.createClass({
        propTypes: {
            optionalArray: React.PropTypes.array,
            optionalBool: React.PropTypes.bool,
            optionalFunc: React.PropTypes.func,
            optionalNumber: React.PropTypes.number,
            optionalObject: React.PropTypes.object,
            optionalString: React.PropTypes.string,
            // 任何可以被渲染的包括,數(shù)字,字符串,組件,或者數(shù)組
            optionalNode: React.PropTypes.node,
            // React 元素
            optionalElement: React.PropTypes.element,
            // 枚舉
            optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
            // 任意一種類型
            optionalUnion: React.PropTypes.oneOfType([
              React.PropTypes.string,
              React.PropTypes.number,
              React.PropTypes.instanceOf(Message)
            ]),
            // 具體類型的數(shù)組
            optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
            // 具體類型的對(duì)象
            optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
            // 符合定義的對(duì)象
            optionalObjectWithShape: React.PropTypes.shape({
              color: React.PropTypes.string,
              fontSize: React.PropTypes.number
            }),
            requiredFunc: React.PropTypes.func.isRequired,
            requiredAny: React.PropTypes.any.isRequired,
            // 自定義校驗(yàn)
            customProp: function(props, propName, componentName) {}
        }
    });
  1. 屬性傳遞的單向性
    我們已經(jīng)提到過(guò) React 的單向數(shù)據(jù)流模式,數(shù)據(jù)的流動(dòng)管道就是 props,流動(dòng)的方向就是組件的層級(jí)自定向下的方向。所以一個(gè)組件是不能修改自身的屬性的,組件的屬性一定是通過(guò)父組件傳遞而來(lái)(或者默認(rèn)屬性)。
  2. 無(wú)狀態(tài)組件屬性
    對(duì)于無(wú)狀態(tài)組件,可以添加 .propTypes 和 .defaultProps 屬性到函數(shù)上。

1.3.5 組件的嵌套組合

在JSX 實(shí)例子中,當(dāng)我們循環(huán)輸出 todo 列表的時(shí)候,React 會(huì)提示對(duì)于循環(huán)輸出的組件,需要有一個(gè)唯一的 key 屬性。這個(gè)問(wèn)題的原因在于 React 的調(diào)和機(jī)制(Reconciliation)上。

  1. 什么叫調(diào)和?
    在每次數(shù)據(jù)更新過(guò)后,React 會(huì)重新調(diào)用 render 渲染出新的組件結(jié)構(gòu),新的結(jié)構(gòu)應(yīng)用到 DOM 中的過(guò)程就叫做調(diào)和過(guò)程。
  2. 為什么需要調(diào)和?
    假設(shè)我們有一個(gè)輸入組件,這個(gè)時(shí)候我們正聚焦在輸入框中,當(dāng)修改值過(guò)后觸發(fā)事件導(dǎo)致了數(shù)據(jù)改變,數(shù)據(jù)改變導(dǎo)致了重渲染, 這個(gè)時(shí)候輸入框被替換成了新的 DOM。 這個(gè)過(guò)程對(duì)用戶來(lái)說(shuō)應(yīng)該是無(wú)感知的,所以那原來(lái)的聚焦?fàn)顟B(tài)應(yīng)該被保存, 那怎么做到的呢? DOM 都被替換了,輸入狀態(tài),選擇狀態(tài)為什么還能保存。 我們先不急著知道 How,目前只需要知道這就是調(diào)和過(guò)程。
    除了保存狀態(tài)以外,調(diào)和過(guò)程還做了很多 DOM 優(yōu)化。 比如輸出一個(gè)數(shù)組的時(shí)候,數(shù)據(jù)新增加或者減少了一下,或者數(shù)組項(xiàng)值改變了,實(shí)際上我們沒(méi)有必要?jiǎng)h除原來(lái)的 DOM 結(jié)構(gòu),只需要修改 DOM 的值或者刪除 DOM 就能實(shí)現(xiàn)重渲染。
    這就是為什么要有 key 屬性,key 屬性能夠幫助定位 DOM 與數(shù)組元素的關(guān)系,在重渲染的時(shí)候能夠?qū)崿F(xiàn)渲染優(yōu)化。

1.4 Life circle of React Components

14.1 Components

React 中組件有自己的生命周期方法,簡(jiǎn)單理解可以為組件從出生(實(shí)例化) -> 激活 -> 銷毀生命周期 hook。通過(guò)這些 hook 方法可以自定義組件的特性。 除此之外,還可以設(shè)置一些額外的規(guī)格配置。

組件生命周期.png
這些生命周期方法都可以在調(diào)用 React.createClass 的參數(shù)對(duì)象中傳入, 之前使用過(guò)了一些方法:
render getInitialState getDefaultProps propTypes

1.4.2 mixins

Type: array mixins
mixins 可以理解為 React 的插件列表,通過(guò)這種模式在不同組件之間共享方法數(shù)據(jù)或者行為只需共享 mixin 就行,mixins 內(nèi)定義的生命周期方法在組件的生命周期內(nèi)都會(huì)被調(diào)用。

var MyMixin1 = {
    componentDidMount: function() {
        console.log('auto do something when component did mount');
    }
};

var MyMixin2 = {
    someMethod: function() {
        console.log('doSomething');
    }
};

var MyComponnet = React.createClass({
    mixins: [MyMixin1, MyMixin2],
    componentDidMount: function() {
        // 調(diào)用 mixin1 共享的方法
        this.someMethod();
    }
});

1.4.3 statics

Type:object statics
statics可以定義組件的類方法
React 的組件是 面向?qū)ο驩OP 的思維,MyComponent 是一個(gè) class,class 分為類方法和實(shí)例方法,實(shí)例方法可以訪問(wèn) this, 然而類方法不能,所以我們不能在 Class 中返回狀態(tài)或者屬性。

var MyComponent = React.createClass({
  statics: {
    customMethod: function(foo) {
      return foo === 'bar';
    }
  }
});

MyComponent.customMethod('bar');  // true

1.4.4 displayName

Type: string displayName
為了顯示調(diào)試信息,每個(gè)組件都會(huì)有一個(gè)名稱,JSX 在轉(zhuǎn)為 JS 的時(shí)候自動(dòng)的設(shè)置 displayName,當(dāng)然我們也可以自定義 displayName

// Input (JSX):
var MyComponent = React.createClass({ });

// Output (JS):
var MyComponent = React.createClass({displayName: "MyComponent", });

1.4.5 生命周期方法

組件生命周期.png

1.4.6 componentWillMount

void componentWillMount()

  • 條件:第一次渲染階段在調(diào)用 render 方法前會(huì)被調(diào)用
  • 作用:該方法在整個(gè)組件生命周期只會(huì)被調(diào)用一次,所以可以利用該方法做一些組件內(nèi)部的初始化工作

1.4.7 componentDidMount

void componentDidMount()

  • 條件:第一次渲染成功過(guò)后,組件對(duì)應(yīng)的 DOM 已經(jīng)添加到頁(yè)面后調(diào)用
  • 作用:這個(gè)階段表示組件對(duì)應(yīng)的 DOM 已經(jīng)存在,我們可以在這個(gè)時(shí)候做一些依賴 DOM 的操作或者其他的一些如請(qǐng)求數(shù)據(jù),和第三方庫(kù)整合的操作。如果嵌套了子組件,子組件會(huì)比父組件優(yōu)先渲染,所以這個(gè)時(shí)候可以獲取子組件對(duì)應(yīng)的 DOM。

1.4.8 componentWillReceiveProps(newProps)

void componentWillReceiveProps(
   object nextProps
)
  • 條件: 當(dāng)組件獲取新屬性的時(shí)候,第一次渲染不會(huì)調(diào)用
  • 用處: 這個(gè)時(shí)候可以根據(jù)新的屬性來(lái)修改組件狀態(tài)
    componentWillReceiveProps: function(nextProps) {
       this.setState({
           likesIncreasing: nextProps.likeCount > this.props.likeCount
      });
    }

注意: 這個(gè)時(shí)候雖說(shuō)是獲取新屬性,但并不能確定屬性一定改變了,例如一個(gè)組件被多次渲染到 DOM 中,如下面:

    var Component = React.createClass({
        componentWillReceiveProps: function(nextProps) {
            console.log('componentWillReceiveProps', nextProps.data.bar);
        },
        rener: function() {
            return <div> {this.props.data.bar} </div>
        }
    });

    var container = document.getElementById('container');
    var mydata = {bar: 'drinks'};
    ReactDOM.render(<Component data={mydata} />, container);
    ReactDOM.render(<Component data={mydata} />, container);
    ReactDOM.render(<Component data={mydata} />, container);

結(jié)果會(huì)輸出兩次 componentWillReceiveProps,雖然屬性數(shù)據(jù)沒(méi)有改變,但是仍然會(huì)調(diào)用 componentWillReceiveProps 方法。

1.4.9 shouldComponentUpdate(nextProps, nextState)

boolean shouldComponentUpdate(
   object nextProps, object nextState
)
  • 條件: 接收到新屬性或者新狀態(tài)的時(shí)候在 render 前會(huì)被調(diào)用(除了調(diào)用 forceUpdate 和初始化渲染以外)
  • 用處: 該方法讓我們有機(jī)會(huì)決定是否重渲染組件,如果返回 false,那么不會(huì)重渲染組件,借此可以優(yōu)化應(yīng)用性能(在組件很多的情況)。

1.4.10 componentWillUpdate

void componentWillUpdate(
  object nextProps, object nextState
)
  • 條件:當(dāng)組件確定要更新,在 render 之前調(diào)用
  • 用處:這個(gè)時(shí)候可以確定一定會(huì)更新組件,可以執(zhí)行更新前的操作
  • 注意:方法中不能使用 setState ,setState 的操作應(yīng)該在 componentWillReceiveProps 方法中調(diào)用

1.4.11 componentDidUpdate

void componentDidUpdate(
  object prevProps, object prevState
)
  • 條件:更新被應(yīng)用到 DOM 之后
  • 用處:可以執(zhí)行組件更新過(guò)后的操作

1.4.12 生命周期與單向數(shù)據(jù)流

我們知道 React 的核心模式是單向數(shù)據(jù)流,這不僅僅是對(duì)于組件級(jí)別的模式,在組件內(nèi)部 的生命周期中也是應(yīng)該符合單向數(shù)據(jù)的模式。數(shù)據(jù)從組件的屬性流入,再結(jié)合組件的狀態(tài),流入生命周期方法,直到渲染結(jié)束這都應(yīng)該是一個(gè)單向的過(guò)程,其間不能隨意改變組件的狀態(tài)。
組件內(nèi)部數(shù)據(jù)流.png

1.5 React & DOM

1.5.1獲取DOM元素

DOM真正被添加到HTML中的hook為

  • componentDidMount
  • componentDidUpdate

在這兩個(gè) hook 函數(shù)中, 我們可以獲取真正的 DOM 元素,React 提供的獲取方法兩種方式

  1. findDOMNode()
    通過(guò) ReactDOM 提供的 findDOMNode 方法, 傳入?yún)?shù)
var MyComponent = React.createClass({
    render: function() {
        return <div> .... </div>
    },
    componentDidMount: function() {
        var $root = ReactDOM.findDOMNode(this);
        console.log($root);
    }
})

需要注意的是此方法不能應(yīng)用到無(wú)狀態(tài)組件上

  1. Refs
    上面的方法只能獲取到 root 元素,那如果我的 DOM 有很多層級(jí),我想獲取一個(gè)子級(jí)的元素呢?React 提供了 ref 屬性來(lái)實(shí)現(xiàn)這種需求。
    每個(gè)組件實(shí)例都有一個(gè) this.refs 屬性,會(huì)自動(dòng)引用所有包含 ref 屬性組件的 DOM
var MyComponent = React.createClass({
    render: function() {
        return  <div>
                    <button ref="btn">...</button>
                    <a href="" ref="link"></a>
                </div>
    },
    componentDidMount: function() {
        var $btn = this.refs.btn;
        var $link = this.refs.link;
        console.log($btn, $link);
    }
})

1.5.2 DOM事件

  1. 綁定事件
    在 React 中綁定事件的方式很簡(jiǎn)單,只需要在元素中添加事件名稱的屬性已經(jīng)對(duì)應(yīng)的處理函數(shù),如:
var MyComponent = React.creatClass({
    render: function() {
        return  <div>
                    <button onClick={this.onClick}>Click Me</button>
                </div>
    },
    onClick: function() {
        console.log('click me');
    }
});

事件名稱和其他屬性名稱一樣,服從駝峰式命名。

  1. 合成事件(SyntheticEvent)
    在 React 中, 事件的處理由其內(nèi)部自己實(shí)現(xiàn)的事件系統(tǒng)完成,觸發(fā)的事件都叫做 合成事件(SyntheticEvent),事件系統(tǒng)對(duì)瀏覽器做了兼容,其提供的 API 與原生的事件無(wú)異。
boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
DOMEventTarget target
number timeStamp
string type

和原生事件的區(qū)別在于,事件不能異步話,如:

function onClick(event) {
     console.log(event); // => nullified object.
     console.log(event.type); // => "click"
     var eventType = event.type; // => "click"

     setTimeout(function() {
         console.log(event.type); // => null
        console.log(eventType); // => "click"
  }, 0);

     this.setState({clickEvent: event}); // Won't work. this.state.clickEvent will only contain null values.
     this.setState({eventType: event.type}); // You can still export event properties.
}

原因是在事件系統(tǒng)的內(nèi)部實(shí)現(xiàn)當(dāng)中, 一個(gè)事件對(duì)象可能會(huì)被重用(也就是事件做了池化 Pooling)。當(dāng)一個(gè)事件響應(yīng)函數(shù)執(zhí)行過(guò)后,事件的屬性被設(shè)置為 null, 如果想用保持事件的值的話,可以調(diào)用
event.persist()
這樣,屬性會(huì)被保留,并且事件也會(huì)被從池中取出。

  1. 事件捕獲和冒泡
    在 DOM2.0 事件分為捕獲階段和冒泡階段,React 中通常我們注冊(cè)的事件為冒泡事件,如果要注冊(cè)捕獲階段的事件,可以在事件名稱后加 Capture 如:
onClick
onClickCapture
  1. 支持事件列表
粘貼板事件 {
      事件名稱:onCopy onCut onPaste
      屬性:DOMDataTransfer clipboardData
}
編輯事件 {
      事件名稱:onCompositionEnd onCompositionStart onCompositionUpdate
      屬性:string data
}
鍵盤事件 {
      事件名稱:onKeyDown onKeyPress onKeyUp
      屬性: {
        boolean altKey
        number charCode
        boolean ctrlKey
        boolean getModifierState(key)
        string key
        number keyCode
        string locale
        number location
        boolean metaKey
        boolean repeat
        boolean shiftKey
        number which
    }
}
// 焦點(diǎn)事件除了表單元素以外,可以應(yīng)用到所有元素中
焦點(diǎn)事件 {
      事件名稱:onFocus onBlur
      屬性:DOMEventTarget relatedTarget
}
表單事件 {
      事件名稱:onChange onInput onSubmit
}
鼠標(biāo)事件 {
      事件名稱:{
        onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp
    }
      屬性:{
        boolean altKey
        number button
        number buttons
        number clientX
        number clientY
        boolean ctrlKey
        boolean getModifierState(key)
        boolean metaKey
        number pageX
        number pageY
        DOMEventTarget relatedTarget
        number screenX
        number screenY
        boolean shiftKey
    }
}
選擇事件 {
      事件名稱:onSelect
}
觸摸事件 {
      事件名稱:onTouchCancel onTouchEnd onTouchMove onTouchStart
      屬性:{
        boolean altKey
        DOMTouchList changedTouches
        boolean ctrlKey
        boolean getModifierState(key)
        boolean metaKey
        boolean shiftKey
        DOMTouchList targetTouches
        DOMTouchList touches
    }
}
UI 事件 {
      事件名稱:onScroll
      屬性:{
        number detail
        DOMAbstractView view
    }
}
滾輪事件 {
      事件名稱:onWheel
      屬性:{
        number deltaMode
        number deltaX
        number deltaY
        number deltaZ
    }
}
媒體事件 {
      事件名稱:{
        onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting
    }
}
圖像事件 {
      事件名稱:onLoad onError
}
動(dòng)畫事件 {
      事件名稱:onAnimationStart onAnimationEnd onAnimationIteration
      屬性:{
        string animationName
        string pseudoElement
        float elapsedTime
    }
}
漸變事件 {
      事件名稱:onTransitionEnd
      屬性: {
        string propertyName
        string pseudoElement
        float elapsedTime
    }
}

1.5.3 表單事件

在 React 中比較特殊的事件是表單事件,大多數(shù)組件都是通過(guò)屬性和狀態(tài)來(lái)決定的,但是表單組件如 input, select, option 這些組件的狀態(tài)用戶可以修改,在 React 中會(huì)特殊處理這些組件的事件。

  1. onChange 事件
    和普通 HTML 中的 onChange 事件不同, 在原生組件中,只有 input 元素失去焦點(diǎn)才會(huì)觸發(fā) onChange 事件, 在 React 中,只要元素的值被修改就會(huì)觸發(fā) onChange 事件。
var MyComponent = React.createClass({
    getInitialState: function() {
        return {
            value: ''
        }
    },
    render: function() {
        return  <div onChange={this.onChangeBubble}>
                    <input value={this.state.value} onChange={this.onChange}/>
                </div>
    },
    onChange: function(ev) {
        console.log('change: ' + ev.target.value);
        this.setState({
            value: ev.target.value
        });
    },
    // onChange 事件支持所有組件,可以被用于監(jiān)聽冒泡事件
    onChangeBubble: function(ev) {
        console.log('bubble onChange event', + ev.target.value);
    }
})
  1. 交互屬性
    表單組件中能被用戶修改的屬性叫交互屬性,包括:
value => <input> 和 <select> 組件
checked => <input type="checkbox|radio">
selected => <opiton>
  1. textarea
    在 HTML 中,textarea 的值是像如下定義的:
<textarea name="" id="" cols="30" rows="10">
        some value
</textarea>

而在 React 中, TextArea 的使用方式同 input 組件,使用 value 來(lái)設(shè)置值

var MyComponent = function() {
    render: function() {
        return <div>
                    <textarea value={...} onChange={...}/>
               </div>
    }
}
  1. select 組件
    在 React 中 select 組件支持 value 值,value 值還支持多選
  <select value="B">
      <option value="A">Apple</option>
      <option value="B">Banana</option>
      <option value="C">Cranberry</option>
  </select>
  <select multiple={true} value={['B', 'C']}>
      <option value="A">Apple</option>
      <option value="B">Banana</option>
      <option value="C">Cranberry</option>
  </select>
  1. 受控組件
    在 React 中表單組件可分為兩類,受控與非受控組件,受控組件是包含了 value 值的,如:
render: function() {
      return <input type="text" value="....."/>
}

為什么叫受控組件? 因?yàn)檫@個(gè)時(shí)候用戶不能修改 input 的值, input 的值永遠(yuǎn)是 value 固定了的值。如果去掉 value 屬性,那么就可以輸入值了。
那如何修改受控組件的值呢? 如上面的例子中, 添加 onChange 事件,事件內(nèi)修改 value 屬性,value 屬性的值會(huì)被設(shè)置到組件的 value 中。

  1. 非受控組件
    沒(méi)有 value 值的 input
render: function() {
     return <input type="text"/>
}

可以通過(guò) defaultValue 屬性來(lái)設(shè)置默認(rèn)值

render: function() {
      return <input type="text" defaultValue="Default Value">
}

類似的對(duì)于 checkbox 有 defaultChecked 屬性
需要注意的是,默認(rèn)值只適用于第一次渲染,在重渲染階段將不會(huì)適用。

  1. checkbox & radio
    checkbox 和 radio 比較特殊, 如果在 onChange 事件中調(diào)用了 preventDefault ,那么瀏覽器不會(huì)更新 checked 狀態(tài),即便事實(shí)上組件的值已經(jīng) checked 或者 unchecked 了 。
var CheckBox = React.createClass({
      getInitialState: function(){
          return {
              checked: false
        }
    },
    render: function() {
        return  <div>
            <input type="checkbox" 
                checked={this.state.checked} 
                onChange={this.onChange}/>
        </div>
    },
    onChange: function(ev) {
        this.setState({
            checked: true
        });
        ev.preventDefault();
    }
})

這個(gè)例子里邊,checked 雖然更新為 true ,但是 input 的值 checked 為 false
那應(yīng)如何處理 checkbox 呢?

  • 避免調(diào)用 ev.preventDefault
  • 在 setTimeout 中處理 checked 的修改
  • 使用 click 事件

1.5.4 Style屬性

在 React 中,可以直接設(shè)置 style 屬性來(lái)控制樣式,不過(guò)與 HTML 不同的是, 傳入的 style 值為一個(gè)object, 對(duì)象的所有 key 都是駝峰式命名,eg:

render: function() {
    var style = {
        backgroundColor: 'red',
        height: 100,
        width: 100
    }
    return <div style={style}></div>
}

其中還可以看到不同的地方時(shí),為了簡(jiǎn)寫寬度高度值,可以直接設(shè)置數(shù)字,對(duì)應(yīng) 100 -> 100px。如果某些屬性不需要添加 px 后綴,React 也會(huì)自動(dòng)去除。

通過(guò)屬性值駝峰式的原因是 DOM 內(nèi)部訪問(wèn) style 也是駝峰式。如果需要添加瀏覽器前綴瑞 -webkit-、-ms- 大駝峰(除了 ms ), 如:

var divStyle = {
  WebkitTransition: 'all', // 'W' 是大寫
  msTransition: 'all'      // 'ms' 為小寫
};

在以前的前端開發(fā)方式是 樣式結(jié)構(gòu)和邏輯要分離, 而現(xiàn)在 React 中卻有很多人推崇** inline **的樣式。 在我看來(lái)因人而異,React 的這種模式也能做到樣式模塊化,樣式重用(借用 Js 的特點(diǎn))。并且因?yàn)?React 的實(shí)現(xiàn)方式,Inline 樣式的性能甚至比 class 的方式高。

1.6 Flux

1.6.1 Flux 介紹

簡(jiǎn)單來(lái)講,F(xiàn)lux 是 Facebook 引入到 React 中的一種前端架構(gòu),通過(guò)定義其核心單向數(shù)據(jù)流的方式,讓 React 應(yīng)用更加健壯。同時(shí),這種應(yīng)用架構(gòu)也具有普適性,可以應(yīng)用到其他任意前端項(xiàng)目中,甚至可以應(yīng)用到客戶端應(yīng)用開發(fā)中,也就是說(shuō) Flux 更應(yīng)該叫做一種架構(gòu)模式(Pattern)。

1.6.2 MVC 架構(gòu)之痛

MVC 的實(shí)現(xiàn)可能有很多種方式,比較靈活,但基本本質(zhì)不會(huì)改變,只是三者間的數(shù)據(jù)傳遞方向可能會(huì)改變,即便是 MVP 模式也只是 MVC 的變種,所以為了統(tǒng)一我們且以下圖的 MVC 方式來(lái)討論。


MVC.png
  1. 概念
  • Model: 負(fù)責(zé)保存應(yīng)用數(shù)據(jù),和后端交互同步應(yīng)用數(shù)據(jù)
  • View: 負(fù)責(zé)渲染頁(yè)面 HTML DOM
  • Controller: 負(fù)責(zé)連接 View 和 Model , Model 的任何改變會(huì)應(yīng)用到 View 中,View 的操作會(huì)通過(guò) Controller 應(yīng)用到 Model 中
  • 關(guān)系:Model, View, Controller 都是多對(duì)多關(guān)系。
  1. 流程
    以 TODOMVC 為例子用戶添加一個(gè) todo 的交互流程:
    View -> Action -> Controller -> Model -> View
  • View -> Action: 添加按鈕事件或者 input 輸入的提交事件
  • Action -> Controller: 控制器響應(yīng) View 事件
  • Controller -> Model: 控制器依賴 Model, 調(diào)用 Model 添加 todo
  • Model -> View: View 監(jiān)聽 Model 的改變添加 todo 事件,在 HTML 中添加一個(gè)新的 Todo 視圖
  1. 問(wèn)題
    對(duì)于新增一個(gè) todo ,需要編寫一個(gè)視圖渲染處理函數(shù),函數(shù)內(nèi)添加新項(xiàng)目到列表中。同理對(duì)于刪除一個(gè) todo,也會(huì)有一個(gè)處理函數(shù)。當(dāng)業(yè)務(wù)邏輯變多過(guò)后,可能有很多模型需要做增刪改的功能,與之對(duì)應(yīng)的就是我們需要精心構(gòu)建這么多的渲染處理函數(shù)。 這種局部更新模式是高性能的關(guān)鍵所在,但問(wèn)題是:
  • 更新邏輯復(fù)雜,需要編寫大量的局部渲染函數(shù)
  • 問(wèn)題定位困難,頁(yè)面的當(dāng)前狀態(tài)是有數(shù)據(jù)和這些局部更新函數(shù)確定的
  1. 如何解決
    如果渲染函數(shù)只有一個(gè),統(tǒng)一放在 App 控制器中,每次更新重渲染頁(yè)面,這樣的話:
    • 任何數(shù)據(jù)的更新都只用調(diào)用重渲染就行
    • 數(shù)據(jù)和當(dāng)前頁(yè)面的狀態(tài)是唯一確定的
      重渲染也有弊端,會(huì)帶來(lái)嚴(yán)重的性能問(wèn)題,重渲染和局部渲染各有好壞,對(duì) MVC 來(lái)說(shuō)這是一個(gè)兩難的選擇,無(wú)法做到魚和熊掌兼得。

1.6.3 Flux 架構(gòu)

通過(guò) React + Flux 就可以完美解決 MVC 的問(wèn)題。簡(jiǎn)單來(lái)說(shuō)在 Flux 架構(gòu)中直接剝離了控制器層,MVC 架構(gòu)變成了 MV + Flux 架構(gòu)。

  • 重渲染: 在 React 中每次渲染都是重渲染,且不影響頁(yè)面性能,是因?yàn)橹劁秩镜氖?Virtual Dom。這就意味著完全不用去關(guān)系重渲染問(wèn)題,增刪改的渲染都和初始化渲染相同入口
  • 數(shù)據(jù)和狀態(tài)一致性: Store 的數(shù)據(jù)確定應(yīng)用唯一的狀態(tài)
  1. 概念
  • one way data flow

    這是 Flux 架構(gòu)的核心思想,從圖中可以看到,數(shù)據(jù)的流向從action 到 view 的一個(gè)單向流。
    單項(xiàng)數(shù)據(jù)流.png
  • Action

    Action 可以理解為對(duì)應(yīng)用數(shù)據(jù)修改的指令,任何修改應(yīng)用數(shù)據(jù)的行為都必須需通過(guò)觸發(fā) action 來(lái)修改。Action 可以來(lái)自于 View,也可以來(lái)自服務(wù)端的數(shù)據(jù)更新。
    action.png
  • Action creator
    為了抽象 Action ,提供一些輔助的語(yǔ)義化的方法來(lái)創(chuàng)建 Action,這些輔助方法叫做 Action Creator。


    Action creator.png
  • Stores
    應(yīng)用的數(shù)據(jù)中心,所有應(yīng)用數(shù)據(jù)都存放在這里控制,同時(shí)包含數(shù)據(jù)的控制行為,可能包含多個(gè) store.
  • Dispatcher
    action 的控制者,所有 action 都會(huì)通過(guò) dispatcher,由 dispatcher 控制 action 是否應(yīng)該傳入到 store 中,Dispatcher 是一個(gè)單例。
  • View
    頁(yè)面的視圖,對(duì)應(yīng) React 的 Component, 視圖可以觸發(fā) action 到 dispatcher。
    需要區(qū)別出一種叫控制器 View(Controller View)的類型,這種 View 可以知曉 store 數(shù)據(jù),把 store 數(shù)據(jù)轉(zhuǎn)化為自身的狀態(tài),在將數(shù)據(jù)傳遞給其他 view 。 并且可以監(jiān)聽 store 數(shù)據(jù)的改變,當(dāng) store 數(shù)據(jù)改變過(guò)后重新設(shè)置狀態(tài)觸發(fā)重渲染。 可以將控制器 View 對(duì)應(yīng) MVC 中的控制器,但是差別很大,控制器 View 唯一多做的事情就是監(jiān)聽 store 數(shù)據(jù)改變,沒(méi)有其他任何業(yè)務(wù)處理邏輯。
  1. 流程
    同樣以 TODOMVC 的添加 todo 為例,F(xiàn)lux 中的流程為:
View -> Action(Action Creator -> Action) -> Dispatcher -> Store -> Controller View -> View
  • View -> Action: 添加按鈕事件或者 input 輸入的提交事件,View 中將事件轉(zhuǎn)化為 action, action 由 Action Creator 創(chuàng)建。
  • Action -> Dispatcher: action 統(tǒng)一由 Dispatcher 分配
  • Dispatcher -> Store: Dispatcher 分配 action 到 Store
  • Store -> Controller View: 控制器 View 監(jiān)聽 store 的數(shù)據(jù)改變,將數(shù)據(jù)轉(zhuǎn)化為自身屬性
  • Controller View -> View: 數(shù)據(jù)改變自動(dòng)重渲染所有視圖
    與MVC的對(duì)比
  • 渲染策略: 數(shù)據(jù)改變 Flux 自動(dòng)渲染,MVC 手動(dòng)編寫更新函數(shù)
  • 事件觸發(fā)策略: Flux 中所有 action 交給 dispather 分配,MVC 中交給對(duì)應(yīng)的控制器分配
  • Flux 在核心策略上的不同是解決 MVC 架構(gòu)問(wèn)題的關(guān)鍵

1.6.4 理解 Flux 架構(gòu)

Flux 架構(gòu)是非常優(yōu)雅簡(jiǎn)潔的,合理利用了一些優(yōu)秀的架構(gòu)思維

  1. 分而治之(Divide And Conquer)
    數(shù)據(jù)的處理過(guò)程是 Store -> Controller View -> View。 所有數(shù)據(jù)來(lái)自于 Store,頁(yè)面的渲染層級(jí)為 Store 將數(shù)據(jù)傳入 Controller View, 再由 Controller View 傳入子 View , 一直到 View 的葉子節(jié)點(diǎn)。
    這個(gè)是一個(gè)典型的分而治之策略,將大的頁(yè)面拆分為小的模塊,再由小的模塊拆分為小的組件,具體組件負(fù)者組件自身的問(wèn)題,所有子組件都是自私的,不用關(guān)心“大家”,只用關(guān)心“小家”。
  2. 合而治之 - 中心化控制
    Flux 把所有的 View 都視作愚民,Store 視作資源的擁有者為統(tǒng)治者,統(tǒng)治者需要提供資源(數(shù)據(jù))給平民,但是如果平民企圖對(duì)資源修改(Mutation),必須得先通知給統(tǒng)治者,讓統(tǒng)治者決定是否做處理。
    我們?yōu)?Flux 中的概念分配角色
    View: 平民
    Action: 資源修改操作
    Dispatcher: 審核官
    Store: 統(tǒng)治者
    一個(gè)企圖修改資源的操作可以描述為:
View Require Mutation -> Action -> Dispatcher -> Store -> Mutate Handler

平民提交 Mutation 請(qǐng)求,由審核官控制,審核通過(guò)后遞交給統(tǒng)治者,統(tǒng)治者再分配給親信做資源 Mutation
合而治之的策略也等于中心化控制策略, 作為統(tǒng)治者既要懂得放權(quán)利(資源的分配),也要懂得控制權(quán)利(資源的修改),這種收縮自如的合理性是 Flux 簡(jiǎn)潔的根本。
同時(shí)這種思維帶來(lái)的優(yōu)點(diǎn)如下:

  • View 的獨(dú)立性和簡(jiǎn)單性:View 自身的邏輯簡(jiǎn)單,不需要知道太多事情,只關(guān)心上級(jí)傳來(lái)的數(shù)據(jù),這種模式使得 View 是低耦合的,簡(jiǎn)潔的。
  • 高可維護(hù)性:中心化控制知道所有對(duì)資源的操作,如果發(fā)生 bug, 可以很快定位問(wèn)題
  1. 函數(shù)式編程思想
    在 Flux 中數(shù)據(jù)的單向流動(dòng)依賴于 View 的確定性,相同的數(shù)據(jù)傳入相同的組件,得到的結(jié)果必然要相同,這是函數(shù)式編程的思想。
    為了保證組件也能做到 “純函數(shù)” 的特性,相同的屬性會(huì)得到相同的渲染結(jié)果。 在寫 React 組件的時(shí)候盡量準(zhǔn)守一下約定:
1.盡量使用無(wú)狀態(tài)組件
2.除了控制類組件以外其他組件避免使用組件狀態(tài)
3.可以通過(guò)屬性計(jì)算出來(lái)的狀態(tài)不要用狀態(tài)來(lái)表示
4.組件的渲染避免外部依賴,按照純函數(shù)的方式寫

函數(shù)式的優(yōu)點(diǎn)也是無(wú)副作用組件的優(yōu)點(diǎn):

  • 無(wú)耦合,可移植性強(qiáng): 組件可重用性高
  • 可測(cè)試性高:組件無(wú)依賴,可以很容易的單獨(dú)測(cè)試組件
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 自己最近的項(xiàng)目是基于react的,于是讀了一遍react的文檔,做了一些記錄(除了REFERENCE部分還沒(méi)開始讀...
    潘逸飛閱讀 3,747評(píng)論 1 10
  • GUIDS 第一章 為什么使用React? React 一個(gè)提供了用戶接口的JavaScript庫(kù)。 誕生于Fac...
    jplyue閱讀 3,716評(píng)論 1 11
  • react 基本概念解析 react 的組件聲明周期 react 高階組件,context, redux 等高級(jí)...
    南航閱讀 1,141評(píng)論 0 1
  • It's a common pattern in React to wrap a component in an ...
    jplyue閱讀 3,409評(píng)論 0 2
  • 青春是個(gè)很有意思的時(shí)期,詩(shī)和遠(yuǎn)方、夢(mèng)想和姑娘占據(jù)了半壁江山。那是個(gè)神奇的年代,一無(wú)所有的年紀(jì)里就敢給心愛(ài)的姑娘許諾...
    碼字的黃小邪閱讀 625評(píng)論 2 4

友情鏈接更多精彩內(nèi)容