React官網的RECENT POSTS閱讀

⭐️寫在開頭

  • 閱讀React官網的 RECENT POSTS的我的翻譯/摘要(部分)。javascript

  • 英文片斷爲官網原文片斷。html

  • 原文地址java

⭐️爲何要使用React

目前已經有不少的JavaScript MVC frameworks出世,可是爲何Facebook須要建立React,而且又是什麼緣由致使咱們想要去用它?node

  • 不是一個MVC框架。是一個可用來構建組件化用戶界面的庫,同時力挺用於構建那些交互數據隨時間改變的可複用的UI組件。python

  • 不是模板。傳統的頁面渲染(JavaScript渲染),是使用一些模板工具或者直接使用html標籤進行:填充+拼接+渲染。這些構建UI的模板總體抽象了你想要的。react

Traditionally, web application UIs are built using templates or HTML directives. These templates dictate the full set of abstractions that you are allowed to use to build your UI.git

  • React以不一樣的方式構建用戶界面:分隔整個UI爲一塊一塊小的UI組件UI Components。這就意味着咱們使用一門真實、充滿特點的編程語言來渲染視圖,其優於模板渲染有如下幾個緣由:github

    • javaScript是一門靈活、強大的編程語言,其擁有構建抽象對象的能力。這一點在大型應用中很是重要。web

    • 經過將標記(標籤)與相應的視圖邏輯統一塊兒來,React可以更加容易的進行擴展和維護視圖。chrome

    • 經過JavaScript code的形式來代替標記(標籤)和內容,這個過程沒有字符拼接的過程而且減小了XSS漏洞。

同時建立了JSX,它是JavaScript的語法擴展。若是好比原生的JavaScript,你更喜歡HTML的可讀性,就使用它吧!


不能再簡單的響應式數據更新

當你的應用數據隨着時間變化時,React表現是十分耀眼!

在傳統的JavaScript應用中,你須要關注哪些數據變化了而且命令式的改變DOM的狀態,以便保持視圖和數據的一致。甚至經過指令和數據來提供了一種聲明式界面的AngularJS,也是經過綁定須要的函數來手動更新DOM節點。

React採用不一樣的方法:

  • 當組件初始化時調用render方法,而且生成一個輕量級的視圖表達式。這個表達式將會返回一個標籤字符串,而且插入頁面文檔中去。

  • 當數據變化的時候,render方法將被會再次調用。爲了儘量的提升更新效率,咱們把先舊數據和新數據傳入render方法進行對比,生成一個最小的變化來操做DOM。(render方法返回的值不是字符串也不是一個DOM元素,而是一個輕量級的DOM表達式)。


HTML僅僅只一個開始

因爲React有着本身的輕量級的文本表達式,咱們能夠作一些更酷的事情:

  • FaceBook使用React代替html繪製canvas動態圖。

  • 結合React和Backbone.Rounter能夠完美的構建單頁應用(Instagram)。

  • 咱們在React內部原理處理,使得React App既能在web上運行,也能經過Objective-C bridge驅動原生IOS視圖。

  • You can run React on the server for SEO, performance, code sharing and overall flexibility。

  • 事件行爲在瀏覽器中保持一次,而且符合標準。默認使用事件代理。

⭐️React v0.3.3

咱們將在React v0.4中新增了許多中西,可是與此同時咱們發佈了React v0.3.3。這個版本解決了人們在使用中遇到的問題,而且使咱們的工具更加容易使用。


React

容許反覆使用同一個DOMNode渲染不一樣的組件。

React.renderComponent(<div/>, domNode)

React.renderComponent(<span/>, domNode)

⭐️New in React v0.4: Prop Validation and Default Values

咱們收到關於React問題中,有大部分是關於props的,特別是人們但願對props進行驗證或者使其有個合理的默認值。


Validation

在使用props以前,咱們常常對props的某些參數進行某些特殊的處理,例如:須要肯定這些屬性是不是特定的類型、需求限定某些值、某些參數的成分是必須的。這些驗證咱們均可以在render裏面處理,可是這回使render顯得臃腫!

如今,React v.04帶來的內建的驗證模塊,你能夠寫你本身的限定

React.createClass({
  propTypes: {
    // An optional string prop named "description".
    description: React.PropTypes.string,
    // A required enum prop named "category".
    category: React.PropTypes.oneOf(['News','Photos']).isRequired,
    // A prop named "dialog" that requires an instance of Dialog.
    dialog: React.PropTypes.instanceOf(Dialog).isRequired
  },
  ...
});

Default Values

在以往的例子裏面,咱們常常看到如下代碼:

React.createClass({
  render: function() {
    var value = this.props.value || 'default value';
    return <div>{value}</div>;
  }
});

若是對幾個穿插在幾個不一樣組件的props進行以上操做,這將會產生大量的冗餘代碼。在React v0.04中,你能夠以申明的方式提供默認值。

React.createClass({
  getDefaultProps: function() {
    return {
      value: 'default value'
    };
  }
  ...
});

在render以前,咱們使用這些函數進行處理(咱們也會在render以前支持全部的驗證函數),以保證你在使用的時候拿到的數據是你所須要的。

Both of these features are entirely optional. We've found them to be increasingly valuable at Facebook as our applications grow and evolve, and we hope others find them useful as well.

⭐️React v0.4.1

React v0.4.1版本只是進行了小小的改動。主要是進行修復。部分代碼在底層修改了,可是這不會影響你調用咱們的公共Api。


React

  • setState的callback參數,會在組件域調用。

  • click事件已經在移動Safari上支持。

  • 阻止已經在Object.prototype上存在的事件錯誤處理。

  • 不用設置先前就已經定義了的DOM屬性爲undefined。

  • 優化對iframe屬性的支持。

  • 增長checksums屬性來檢測和校驗服務端渲染和客戶端實際渲染內容的是否一致。


JSXTransformer

改良環境檢測機制,使其能運行在非瀏覽器環境。

Improved environment detection so it can be run in a non-browser environment.

⭐️Use React and JSX in Python Applications

今天咱們很高興的宣佈PyReact的初始版本的發佈。它使得在你的pyhton應用中可以很好的使用ReactJSXPyReact設計的宗旨是提供一個轉化JSXJavaScript的API。


Usage:show me code

from react import jsx
# For multiple paths, use the JSXTransformer class.
transformer = jsx.JSXTransformer()
for jsx_path, js_path in my_paths:
    transformer.transform(jsx_path, js_path)
# For a single file, you can use a shortcut method.
jsx.transform('path/to/input/file.jsx', 'path/to/output/file.js')

Django:support the pip

#install
$ pip install PyReact
#use
PIPELINE_COMPILERS = ( 'react.utils.pipeline.JSXCompiler',)

React Page

Jordan Walke 實現了一個完整的react項目react-page。其支持客戶端和服務端的渲染、使用模板系統進行轉化和打包、實時重載。

爲何使用服務端渲染?

  • 提升頁面初始化速度

    • 在下載JavaScript前,一些標籤元素將會被展現給用戶。

    • 在服務端標籤元素的構建遠遠快於客戶端。

  • 更快的開發和構建原型

  • 不用等待任何監聽腳本或打包器。

  • 更容易的部署

  • SEO

服務端渲染是如何進行的?

  • 在服務端構建、發送標籤給客戶端,這樣用戶能夠很快的看到內容。

  • 而後對應的JavaScript將會被打包送往客戶端。

  • 瀏覽器運行對應JavaScript,這樣全部的事件句柄、交互、數據更新等將會無縫隙的對服務端生成的標記進行綁定。

  • 肉眼看來這一切好像都是發生在客戶端,只是更快。

⭐️ React v0.5


Changelog

  • 優化內存使用:經過使用 GC PAUSES(GC:Garbage Collection,垃圾收集,垃圾回收)機制,減小核心內存分配。

  • 提高性能:爲了提供代碼運行速度,咱們從V8和Nitro中剔除了一些slow path code

在網絡設備裏面區分不一樣的路徑是一個很天然的選擇,由於網絡設備的首要任務是轉發網絡包,不一樣的網絡包,在設備裏面的處理路徑不一樣。fast path就是那些能夠依據已有狀態轉發的路徑,在這些路徑上,網關,二層地址等都已經準備好了,不須要緩存數據包,而是能夠直接轉發。slow path是那些須要額外信息的包,好比查找路由,解析MAC地址等。

first path是設備收到流上第一個包所走過的路徑,好比tcp裏面的syn包,有些實現把三次握手都放到first path裏面處理,而有些只需處理syn包,其餘包就進入fast path的處理路徑。

在NP或者ASIC裏面也須要區分slow pathfast pathfast path上的包都放在SRAM裏面,而slow path的包放在DRAM裏面。不過這也非絕對。決定處理路徑的是對於速度的考慮。處理器的速度,內存的速度等。訪問內存的速度由速度和帶寬兩個因素決定。所以,把SRAM放到fast path上,也是很天然的選擇。

fast path: Frontend -> simple code generator -> assembler
slow path: Frontend -> IR optimizer (sometimes more than one level of IR) -> code-generator -> assembler

  • 標準化支持:DOM屬性的處理。在處理以前不只有多餘的檢查和開銷,並且仍是混淆使用者。如今咱們會一直控制你的屬性值爲string直到渲染。

  • 支持Selection事件。

  • 支持混合事件(池化喲)。

  • 支持一些額外DOM屬性:charSet, content, form, httpEquiv, rowSpan, autoCapitalize

  • 支持額外的SVG屬性:rxry

  • 同時支持getInitialStategetDefaultProps

  • 支持掛載到iframes

  • 表單組件的bugfix

  • 增長react.version

  • 增長react.isValidClass:用戶檢查某個值是不是有效的組件構造函數。

  • 刪除React.autoBind。在React v0.4中棄用,如今正式刪除。

  • 重命名:React.unmountAndReleaseReactRootNodeReact.unmountComponentAtNode

  • 開始着手於精細的性能分析。

  • 更好的支持服務端渲染。

  • 是React運行於一個嚴格的安全策略下成爲可能。同時使使用React編寫chrome插件成爲可能。


JSX

  • 屬性class改名問className

  • 增長警告提示(非生產環境)。

  • 改善Window兼容性。

⭐️ React v0.9 [RC]

Upgrade Notes

咱們JSX解釋器處理空格上面進行了改變。簡單來講就是,在一行上面組件與組件以前的空格將會被保留,而換行(文本節點或者組件)會被忽略。

<div>
  Monkeys:
  {listOfMonkeys} {submitButton}
</div>
0.8-
React.DOM.div(null,
  " Monkeys: ",
  listOfMonkeys, submitButton
)
0.9+
React.DOM.div(null,
  "Monkeys:",
  listOfMonkeys, " ", submitButton
)

相信這個新特性會很是有空,能夠有效減小匆忙之中帶來的非期待的空格。

若是你但願保留 後面緊跟換行文本節點 後面的空格,那麼你能夠在JSX裏面使用這種寫法:{"Monkeys: }

⭐️ The Road to 1.0 (2014-03-28)

咱們在去年春天發佈了React,可是咱們卻有意的沒有發佈版本React v1.0。其實咱們已經作好了生產準備,可是咱們計劃在內部和外部根據開發者怎麼使用React來發展API和行爲特性。在過去的九個月咱們學到了不少,而且思考不少關於1.0對於React意味着什麼。在過去的兩週內,我在多個項目裏面概述了咱們打算進入1.0+的世界。今天我書寫一點點給咱們的用戶,以便用戶更好的瞭解咱們的計劃。

咱們在1.0中主要的目的是闡明咱們的消息模式,而且聚焦在一個與咱們目的相關的API上面進行處理。爲了完成這個目的,咱們清除一些已經遇到的不友好的模式,真正的幫助開發者寫出更好的代碼。


ES6

在咱們正式推出React以前,咱們就思考過如何在React裏面利用ES6,即類,以此來提升建立React組件的體驗。咱們以爲使用 React.createClass(…) 不是一個最好的選擇。即便在使用方面優劣性沒有正確的答案,可是咱們在向ES6方面靠攏。咱們但願確保這個過程儘量的簡單。例如:

class MyComponent extends React.Component {
  render() {
    ...
  }
}

其餘一些ES6的特性咱們在React核心使用了。確信後面會有更多的特性加入。

JSXES6方面的支持咱們已經在react-tools裏面搭船上線。已經支持轉化大部分的ES6代碼轉換爲老瀏覽器支持的代碼。


Context

儘管咱們沒有在文檔中說起過context,可是它以某種形式在React確切存在的。

While we haven't documented context, it exists in some form in React already. It exists as a way to pass values through a tree without having to use props at every single point. We've seen this need crop up time and time again, so we want to make this as easy as possible. Its use has performance tradeoffs, and there are known weaknesses in our implementation, so we want to make sure this is a solid feature.

⭐️ React v0.11


getDefaultProps

從React0.11開始,getDefaultProps()只會在 React.createClass() 調用的時候調用一次,替代原來每次組件渲染時調用。這就意味着 getDefaultProps() 不會改變他的返回值,同時任何對象將會在全部實例中共享。這個改變提示了性能,而且使得未來可以更早的在渲染中作 PropTypes check ,這將使咱們可以給出更好的錯誤提示。


Rendering to null

自從React發佈以來,開發者基本都遇到過 render nothing 的狀況。一般是返回一個空<div/>或者<span/>。有些更聰明的人返回<noscript/>來避免不要的DOM nodes。咱們對此提供了一個解決辦法:return null 。這樣可以進一步幫忙開發者寫出有意義的代碼。在實現上,咱們使用了<noscript>標籤進行處理,儘管咱們目的是不返回任何東西。由於noscript並不會影響你的佈局,因此你能夠放心使用。

// Before
render: function() {
  if (!this.state.visible) {
    return <span/>;
  }
  // ...
}
// After
render: function() {
  if (!this.state.visible) {
    return null;
  }
  // ...
}

JSX Namespacing

JSX裏面支持namespaceing呼喊咱們已經聽到了很長一段時間。考慮到JSX是JavaScript實現的,因此咱們不肯意使用XML namespaceing。相反,咱們使用選擇JavaScript標準來實現: object property access。替代爲每一個組件分配一個對象,你能夠直接這樣使用 <Namespace.Component />

// Before
var UI = require('UI');
var UILayout = UI.Layout;
var UIButton = UI.Button;
var UILabel = UI.Label;
render: function() {
  return <UILayout><UIButton /><UILabel>text</UILabel></UILayout>;
}
// After
var UI = require('UI');
render: function() {
  return <UI.Layout><UI.Button /><UI.Label>text</UI.Label></UI.Layout>;
}

Improved keyboard event normalization

根據DOM3,React鍵盤事件包含了一個標準化的 e.key ,這樣容許你在代碼中編寫一個簡單的key,而且可以在全部瀏覽器運行。

handleKeyDown: function(e) {
  if (e.key === 'Enter') {
    // Handle enter key
  } else if (e.key === ' ') {
    // Handle spacebar
  } else if (e.key === 'ArrowLeft') {
    // Handle left arrow
  }
}

React鍵盤事件和鼠標事件也包含了標準化的 e.getModifierState()

⭐️ Flux: Actions and the Dispatcher

Flux是Facebook構建JavaScript應用的時候使用的基於單項數據流的應用框架。咱們使用Flux的構建大型應用都是有小的組件組成,Flux來控制咱們提供的小組件(們)。咱們找到了一個很是棒的代碼組織結構,咱們十分激動的分享到開源社區。

好比一個完整的框架,Flux更像一種模式,讓你不用增長太多新代碼就可以使用Flux。直到最近的,咱們尚未發佈Flux模塊之一: dispatcher。可是隨着新的Flux code項目和Flux website發佈,咱們提供咱們在生產項目中使用的dispatcher


Where the Dispatcher Fits in the Flux Data Flow

dispatcher是一個單例。做爲數據控制中心控制Flux應用的數據流。簡單的來講,他就是提供註冊回調函數,而後使用必定的命令調用這些回調函數。每一個store都經過dispatcher註冊了回調。當dispatcher中有新數據來,它使用這些回調通知相應的store。而後相關程序經過dispatch()啓動回調函數。


Actions and ActionCreators

不管是用戶進行界面操做仍是接口返回的新數據進入系統的時候,這些數據將會被打包送入一個 action(由內容和action type組成的對象)。對此,咱們經常建立名爲ActionCreateors的輔助庫,用來建立action object和傳遞actiondispatcher

不一樣的actioins由一個type屬性定義。當全部的stores收到action的時候,它們就使用這個屬性來決定怎麼響應它。在Flux應用中,storesviews彼此自我控制,它們不會被外部影響。操做流經過stores定義註冊的回調進入store, not through setter methods。

使stores自我更新可以避免不少一些MVC應用的複雜狀況,例如:各個models之間的聯合更新會致使狀態的不穩定而且會致使測試很是困難。objectsFlux應用中高度分離,而且嚴格準守得墨忒耳定律。這樣會致使軟件更加可維護、適配、測試以及對新工程師來誰更加容易理解。


Why We Need a Dispatcher

隨着應用的壯大,不一樣stores之間的依賴必然存在。例如:Store A 必須 Store A 先更新,而後 Store A才知道如何去更新本身。這個時候咱們就須要dispatcher可以調用 Store B的回調,以後再操做Store A。爲了申明這種依賴,Store A 須要告訴dispatcher,我須要等待Store B完成後才能執行這個actiondispatcher經過 waitFor()提供這樣的功能。

dispatch()方法經過回調函數提供了一個簡單的、同步迭代功能:依次調用。當waitFor()在某一個回調中觸發,隨後中止執行這個回調函數,而且 waitFor()將提供咱們一個有關依賴的新的迭代週期。等這些依賴執行完之後,回調函數再繼續執行。

更者,waitFor()方法但是在同一個store中不不一樣的actions間調用。

Problems arise, however, if we have circular dependencies. That is, if Store A needs to wait for Store B, and Store B needs to wait for Store A, we could wind up in an endless loop. The dispatcher now available in the Flux repo protects against this by throwing an informative error to alert the developer that this problem has occurred. The developer can then create a third store and resolve the circular dependency.

⭐️Introducing React Elements

If you currently use JSX everywhere, you don't really have to do anything to get these benefits! The updated transformer will do it for you.
If you can't or don't want to use JSX, then please insert some hints for us. Add a React.createFactory call around your imported class when you require it。


New Terminolog

咱們爲了使新用戶更簡單的瞭解DOM(和React的不一樣之處)。咱們使用術語ReactElement代替<>,一樣ReactNode代替renderable


Creating a ReactElement

咱們提供一個API來建立ReactElement

var reactElement = React.createElement(type,props,children);

type參數可使HTML tag或者class。它指示着什麼樣的HTML tag或者class將被渲染和包含哪些props數據。你也能夠只提供一個type參數建立一個工程函數。

var div = React.createFactory('div');
var reactDivElement = div(props,children);

React Element的簽名就像這樣

{
  type : string | class,
  props : { children, className, etc. },
  key : string | boolean | number | null,
  ref : string | null
}

Upgrading to 0.12

React With JSX

若是你使用ReactJSX轉化器,這個升級將會很是簡單。

// If you use node/browserify modules make sure
// that you require React into scope.
var React = require('react');

React的JSX將會爲你建立ReactElement

var MyComponent = React.createClass(...);
var MyOtherComponent = React.createClass({
  render: function() {
    return <MyComponent prop="value" />;
  }
});

React Without JSX

在不使用JSX狀況下須要調用一個組件做爲函數,在調用前你須要明確的建立一個工程函數。

var MyComponentClass = React.createClass(...);
var MyComponent = React.createFactory(MyComponentClass); // New step
var MyOtherComponent = React.createClass({
  render: function() {
    return MyComponent({ prop: 'value' });
  }
});

React爲常見的HTML elements內置了工廠函數。

var MyDOMComponent = React.createClass({
  render: function() {
    return React.DOM.div({ className: 'foo' }); // still ok
  }
});

The Next Step: ES6 Classes

在v0.12版本後,咱們的工做將轉向ES6 classes。咱們會保持向後兼容(React.createclass)。若是你已經在使用ES6轉譯器,你能夠按照下面申明你的組件。

export class MyComponent {
    render(){
        ...
    }
};

⭐️Deprecating JSTransform and react-tools

隨着JavaScript的發展,JSTransform有點"跟不上時代",Babel的出現能夠徹底將其替代。v0.14之後將不會再維護JSTransformreact-toolsreact-tools has always been a very thin wrapper around JSTransform.),ReactReact Native目前都使用三方的Babel的JSX編譯器處理.


Other Deprecations

esprima-fb

ECMAScript解析器

JSXTransformer

JSXTransformer is another tool we built specifically for consuming JSX in the browser. It was always intended as a quick way to prototype code before setting up a build process. It would look for <script> tags with type="text/jsx" and then transform and run. This ran the same code that react-tools ran on the server.

⭐️ReactDOM.render and the Top Level React API

React的世界裏面全部都是組件,可是你涉及的代碼、工程並非都是由React來構建的。因此,須要你編寫管道代碼進行二者的連接。處理這些事情的主要API爲:

ReactDOM.render(reactElment,domContainerNode)

這須要你提供一個額外的DOM容器。

當把React插入在單頁應用中時候,須要你手動的控制生命週期。React不會自動釋放元素,須要手動控制:

ReactDOM.unmountComponentAtNode(domComtainerNode)

It is not unique to the DOM. If you want to insert a React Native view in the middle of an existing iOS app you will hit similar issues.

Object Oriented Updates

若是你調用ReactDOM.render屢次,那麼對應組件上次的props將會被最新的徹底替代。

ReactDOM.render(<App locale="en-US" userID={1} />, container);
// props.userID == 1
// props.locale == "en-US"
ReactDOM.render(<App userID={2} />, container);
// props.userID == 2
// props.locale == undefined ??!?

在面向對象編程中,全部的狀態依賴實例存在,經過控制狀態的改變控制應用變化。若是你在一個使用面向對象API的app裏面使用React,你也許會驚訝或者迷茫當你設置一個屬性的時候會致使其餘屬性的消失。

對此咱們提供了一個輔助函數setProps來容許你一次只更新所指望的屬性。

Unfortunately this API lived on a component instance, required React to keep this state internally and wasn't very natural anyway. Therefore, we're deprecating it and suggest that you build it into your own wrapper instead.

class ReactComponentRenderer {
  constructor(klass, container) {
    this.klass = klass;
    this.container = container;
    this.props = {};
    this.component = null;
  }
  replaceProps(props, callback) {
    this.props = {};
    this.setProps(props, callback);
  }
  setProps(partialProps, callback) {
    if (this.klass == null) {
      console.warn(
        'setProps(...): Can only update a mounted or ' +
        'mounting component. This usually means you called setProps() on ' +
        'an unmounted component. This is a no-op.'
      );
      return;
    }
    Object.assign(this.props, partialProps);
    var element = React.createElement(this.klass, this.props);
    this.component = ReactDOM.render(element, this.container, callback);
  }
  unmount() {
    ReactDOM.unmountComponentAtNode(this.container);
    this.klass = null;
  }
}

Object-oriented APIs don't look like that though. They use setters and methods. I think we can do better. If you know more about the component API that you're rendering, you can create a more natural object-oriented API around your React component.

class ReactVideoPlayer {
  constructor(url, container) {
    this._container = container;
    this._url = url;
    this._isPlaying = false;
    this._render();
  }
  _render() {
    ReactDOM.render(
      <VideoPlayer url={this._url} playing={this._isPlaying} />,
      this._container
    );
  }
  get url() {
    return this._url;
  }
  set url(value) {
    this._url = value;
    this._render();
  }
  play() {
    this._isPlaying = true;
    this._render();
  }
  pause() {
    this._isPlaying = false;
    this._render();
  }
  destroy() {
    ReactDOM.unmountComponentAtNode(this._container);
  }
}

⭐️React Components, Elements, and Instances

ComponentsElementsComponent instrances三者的不一樣也許迷惑着許多初學者。爲何三個不一樣的事物合做可以在屏幕上繪圖。

Managing the Instances

若是你是初學者,你也許開始是和Component classesComponent classes Instances打交道。慄如,建立class來申明Button組件。當應用運行時,你可能有Button組件的多個實例,每一個實例有着本身的屬性和本地狀態。這是傳統的面向對象的UI編程。

在傳統的UI模塊中,由你來控制建立和銷燬組件實例。慄:若是一個Form組件要渲染一個Button組件。它須要一個Button的實例而且手動保持和任何信息的交流。

class Form extends TraditionalObjectOrientedView {
  render() {
    // Read some data passed to the view
    const { isSubmitted, buttonText } = this.attrs;
    if (!isSubmitted && !this.button) {
      // Form is not yet submitted. Create the button!
      this.button = new Button({
        children: buttonText,
        color: 'blue'
      });
      this.el.appendChild(this.button.el);
    }
    if (this.button) {
      // The button is visible. Update its text!
      this.button.attrs.children = buttonText;
      this.button.render();
    }
    if (isSubmitted && this.button) {
      // Form was submitted. Destroy the button!
      this.el.removeChild(this.button.el);
      this.button.destroy();
    }
    if (isSubmitted && !this.message) {
      // Form was submitted. Show the success message!
      this.message = new Message({ text: 'Success!' });
      this.el.appendChild(this.message.el);
    }
  }
}

上面的僞複合UI代碼(或者增長更多)都是按照面向對象的方式使用庫,就像Backbone同樣。每一個組件實例都保持DOM nodeChild Component關聯而且在合適的時間建立/銷燬(DOM nodeChild Component)。隨着代碼量的增長,組件可能的狀態數會按平方級增加,而且父級能夠直接訪問子組件的實例,未來想要解偶就會變的很是困難。

比起上述,React有什麼不一樣?

Elements Describe the Tree

爲解決上述問題,React裏面出現了ElementsAn element is a plain object describing a component instance or DOM node and its desired properties.它包含了一些惟一的信息:組件類型(Button)、屬性(color)和擁有的Child Elements

PlainObject:JSON形式定義的普通對象或者new Object()建立的簡單對象。

一個Element不是一個實際的實例。相反,他是一種告訴React什麼須要渲染在屏幕上的方式。你不能調用任何在Elements上面的方法,它僅僅是一個擁有兩個字段屬性的不可變對象(type : ( string | ReactClass )props : Object)。

DOM Elements

Elementtype是一個字符串時,它表明着一個擁有props做爲屬性的DOM node(這些就是React將會渲染的)。栗子:

{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}
⇩
<button class='button button-blue'>
  <b>
    OK!
  </b>
</button>

對於Elements嵌套的問題,按照慣例,咱們但願建立一個Elements樹,而後指定一個或者多個Child Elements做爲其容器/父元素的children props

最重要的一點仍是Child ElementsParent Elements僅僅只一中描述方式並非真實實例。當你建立它們的時候並不涉及在屏幕上面呈現的東西。建立和丟棄他們都是可有可無的。

React elements are easy to traverse, don’t need to be parsed, and of course they are much lighter than the actual DOM elements—they’re just objects!

Component Elements

而後Elementstype參數可使一個React Component對應的函數或者類。

{
    type : Button,
    props : {
        color : "blue",
        children : "OK!",
    }
}

這是React的核心思想。

An element describing a component is also an element, just like an element describing the DOM node. They can be nested and mixed with each other.

這個特性可讓你建立一個DangerButton,它是一個擁有特定color屬性值的Button組件。而且不用小心Button是否渲染爲DOM <button><div>或者其餘的。

const DangerButton = ({ children }) => ({
  type: Button,
  props: {
    color: 'red',
    children: children
  }
});

你能夠混合搭配DOM/Component Elements在一個簡單的Element Tree裏面。

const DeleteAccount = () => ({
  type: 'div',
  props: {
    children: [{
      type: 'p',
      props: {
        children: 'Are you sure?'
      }
    }, {
      type: DangerButton,
      props: {
        children: 'Yep'
      }
    }, {
      type: Button,
      props: {
        color: 'blue',
        children: 'Cancel'
      }
   }]
});

或者你使用JSX

const DeleteAccount = () => (
  <div>
    <p>Are you sure?</p>
    <DangerButton>Yep</DangerButton>
    <Button color='blue'>Cancel</Button>
  </div>
);

這種混合和匹配有助於下降組件之間的耦合度,所以它們徹底能夠經過一下的結構同時表達is-a和has-a的關係

  • Button組件是一個具備特定屬性的DOM元素button;

  • DangerButton組件是一個具備特定屬性的Button組件;

  • DeleteAccount在一個div元素中包含一個Button組件和一個DangerButton組件。

Components Encapsulate Element Trees

React收到一個使用函數或者類做爲type值得Element的時候,它知道詢問對應的組件什麼樣的Element須要被渲染,並給予相應的props

當看到這個Element的時候

{
  type: Button,
  props: {
    color: 'blue',
    children: 'OK!'
  }
}

React將會詢問Button Component什麼須要渲染。Button Component將會返回

{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}

React將重複此過程,直到它知道頁面上的每一個組件有用的底層DOM標籤元素。

前面提到的Form實例使用React能夠這樣

const Form = ({ isSubmitted, buttonText }) => {
  if (isSubmitted) {
    // Form submitted! Return a message element.
    return {
      type: Message,
      props: {
        text: 'Success!'
      }
    };
  }
  // Form is still visible! Return a button element.
  return {
    type: Button,
    props: {
      children: buttonText,
      color: 'blue'
    }
  };
};

以上,對於React Component組件,props是輸入,Element Tree是輸出。

The returned element tree can contain both elements describing DOM nodes, and elements describing other components. This lets you compose independent parts of UI without relying on their internal DOM structure.

返回的元素樹包括描述DOM node及描述其它Component。這可讓咱們獨立地編寫UI部分,而無需依賴它們的內部DOM結構。

咱們使React建立、更新、銷燬實例。

咱們使用Components返回的Elements描述實例,React負責管理實例。

Components Can Be Classes or Functions

// 1) As a function of props
const Button = ({ children, color }) => ({
  type: 'button',
  props: {
    className: 'button button-' + color,
    children: {
      type: 'b',
      props: {
        children: children
      }
    }
  }
});
// 2) Using the React.createClass() factory
const Button = React.createClass({
  render() {
    const { children, color } = this.props;
    return {
      type: 'button',
      props: {
        className: 'button button-' + color,
        children: {
          type: 'b',
          props: {
            children: children
          }
        }
      }
    };
  }
});
// 3) As an ES6 class descending from React.Component
class Button extends React.Component {
  render() {
    const { children, color } = this.props;
    return {
      type: 'button',
      props: {
        className: 'button button-' + color,
        children: {
          type: 'b',
          props: {
            children: children
          }
        }
      }
    };
  }
}

When a component is defined as a class, it is a little bit more powerful than a functional component. It can store some local state and perform custom logic when the corresponding DOM node is created or destroyed.

A functional component is less powerful but is simpler, and acts like a class component with just a single render() method. Unless you need features available only in a class, we encourage you to use functional components instead.

Top-Down Reconciliation

ReactDOM.render({
  type: Form,
  props: {
    isSubmitted: false,
    buttonText: 'OK!'
  }
}, document.getElementById('root'));

你當運行上述代碼的時候,React會詢問Form組件返回的Element Tree,給予對應props

// React: You told me this...
{
  type: Form,
  props: {
    isSubmitted: false,
    buttonText: 'OK!'
  }
}
// React: ...And Form told me this...
{
  type: Button,
  props: {
    children: 'OK!',
    color: 'blue'
  }
}
// React: ...and Button told me this! I guess I'm done.
{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}

This is a part of the process that React calls reconciliation which starts when you call ReactDOM.render() or setState(). By the end of the reconciliation, React knows the result DOM tree, and a renderer like react-dom or react-native applies the minimal set of changes necessary to update the DOM nodes (or the platform-specific views in case of React Native).

You might have noticed that this blog entry talks a lot about components and elements, and not so much about the instances. The truth is, instances have much less importance in React than in most object-oriented UI frameworks.

Summary

Element只是是PlainObject,用來描述呈如今屏幕上的DOM node或其它ComponentElements的props中能夠包含其它Elements。建立ReactReact Element是廉價的,一旦建立了React Element,就不會再發生變化。

一個組件的申明有幾個不一樣的方式:classfunctionReact.creatClass。不管哪一種方式,老是props爲輸入,Element Tree爲輸出。

An instance is what you refer to as this in the component class you write. It is useful for storing local state and reacting to the lifecycle events.(實例就是對組件類中this的引用。)

最後,建立React Elements使用React.createElement()JSX或者Element Factory helper。別在實際代碼中編寫PlainObect形式的Elements(只須要知道在底層他們是PlainObject就好了)。

⭐️(A => B) !=> (B => A)

文檔裏面對於componentWillReceiveProps的陳述爲:componentWillReceiveProps在對應props被改變的時候調用,並做爲rerender的結果。這致使部分用戶認爲:componentWillReceiveProps被調用了對應props必定會變化。邏輯上這個結論是不正確的。

formal logic/mathematics:A包含着B,不表明B包含着A。有不少緣由致使componentWillReceiveProps被調用,即便對應的props沒有改變。

你若是不相信,能夠試試使用準確的props三次調用ReactDOM.render(),而且監控componentWillReceiveProps的調用。

class Component extends React.Component {
  componentWillReceiveProps(nextProps) {
    console.log('componentWillReceiveProps', nextProps.data.bar);
  }
  render() {
    return <div>Bar {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);

以上代碼componentWillReceiveProps會被調用兩次。

爲了理解爲何會這樣,咱們須要想一想會發生什麼。在初始渲染和兩次後續更新之間數據可能已經被改變了,若是代碼像下面這樣執行:

var myData = {
    bar: 'drinks'
};
ReactDOM.render(<Component data={myData} />, container);
myData.bar = 'food';
ReactDOM.render(<Component data={myData} />, container);
myData.bar = 'noise';
ReactDOM.render(<Component data={myData} />, container);

數據沒有改變,但React並無辦法知道。所以,React須要調用componentWillReceiveProps方法,由於組件須要新props來通知(即便新的props和舊props徹底相同)。

你可能會認爲,React可使用很巧妙的檢測機制來檢測是否相等,但這種想法也有一些問題

  • 舊的myData和新的myData其實是相同的物理對象(僅對象內部的值被改變)。因爲採用的是triple-equals-equal,檢查是否相同的時候並不會告訴咱們值是否被改變。唯一可能的解決方法就是建立數據的一個深拷貝副本,接着作深比較,但這對比較大的數據結構而言過於昂貴(特別是循環)

  • myData對象可能包括對函數的引用,該函數經過閉包獲取變量。React沒有辦法獲取閉包內部的變量值,所以也沒有辦法複製和驗證它們是否相等

  • myData可能包括父級渲染時從新實例化了的實例對象的引用,但概念上是相等的(具備相同的keyvalue)。深比較能夠檢測到這一點,除過這點又會出現新的問題,由於沒有辦法比較兩個函數在語義上是否相同。

因爲語言的限制,有時咱們不可能實現真正意義上相等的語義。在這種狀況下,React會調用componentWillReceiveProps方法(即便props可能沒有改變),使得組件有機會檢測新的props,並採起相應的處理。

這樣一來,實現componentWillReceiveProps方法時候要確保props不能被修改。若是你想在props被改變後執行一些操做(如網絡請求),你的componentWillReceiveProps代碼須要檢查props是否真正的被改變了。

相關文章
相關標籤/搜索