React 官網上定義:前端
React 是一個用於構建用戶界面的 JavaScript 庫。react
首先,讓咱們看一下這個定義的兩個不一樣部分:算法
這意味着它不徹底是一個 框架 。它不是一個完整的解決方案,你常常須要使用更多的庫來輔助 React 造成一套完整的解決方案。React 不對解決方案中的其餘部分做任何假設。編程
框架是一個偉大的目標,特別是對於年輕的團隊和初創公司。在使用框架時,已經爲你作出了許多明智的設計決策,這爲你提供了一條清晰的道路,能夠專一於編寫良好的應用程序邏輯。可是,框架存在一些缺點。對於從事大型代碼庫開發工做的,而且經驗豐富的開發人員來講,這些缺點有時會極具破壞性的。數組
儘管有些人聲稱,框架並不靈活。框架一般但願你以某種方式編碼全部內容。若是你試圖偏離這種方式,框架常常會爲此與你發生衝突。框架一般很大而且功能齊全,若是你只須要使用它們中的一小部分,你必需要引入整個框架。不能否認今天這一點正在改變,但仍然不理想,一些框架正在模塊化,我認爲這很棒,但我是純 Unix 哲學的忠實粉絲:瀏覽器
編寫作一件事並作得好的程序。編寫程序以協同工做。 - 道格麥克羅伊
React 遵循 Unix 哲學,由於它是一個小型庫,專一於一件事而且很是好地完成這件事。「一件事」 是React定義的第二部分:構建用戶界面。框架
用戶界面(UI)是展示在用戶面前,用於與機器交互的媒介。用戶界面無處不在,從微波爐上的簡單按鈕到航天飛機的儀表板。若是咱們嘗試鏈接的設備能夠識別 JavaScript ,咱們就可使用 React 來描述它的 UI 。因爲 Web 瀏覽器識別 JavaScript ,咱們可使用 React 來描述 Web UI 。模塊化
咱們只須要告訴瀏覽器咱們想要什麼!React 將表明咱們在 Web 瀏覽器中構建實際的 UI。若是沒有React或相似的庫,咱們須要使用原生 Web API 和 JavaScript 手動構建 UI,這並不容易。函數
當你聽到 React 是聲明 的陳述時,這正是它的含義。咱們用 React 描述 UI 並告訴它咱們想要什麼(而不是如何作)。React將負責「how」並將咱們的聲明性描述(咱們用React語言編寫)轉換爲瀏覽器中的實際UI。React 與 HTML 自己共享這種簡單的聲明能力,可是使用React,咱們能夠聲明表明動態數據的HTML UI,而不只僅是靜態數據。性能
當 React 發佈時,有不少關於它性能的質疑,由於它引入了一個虛擬 DOM 的聰明想法,能夠用來協調實際的DOM(咱們將在下一節討論)。
DOM是文檔對象模型(Document Object Model)。它是HTML(和XML)文檔的瀏覽器編程接口,將它們視爲樹結構。DOM API可用於更改文檔結構,樣式和內容。
雖然今天 React 很是流行的最重要緣由之一就是 React 的高性能,但我並無把它歸類爲 React 的最好的一點。我認爲 React 是一個遊戲規則改變者,由於它在開發人員和瀏覽器之間建立了一種通用語言,容許開發人員以聲明方式描述UI並管理其狀態(state)上的操做,而不是對 DOM 元素的操做。它只是用戶界面「結果」的語言。開發人員只是根據「最終」狀態(如函數)來描述接口,而不是採用步驟來描述接口上的操做。當更新該狀態時,React會根據它來更新 DOM 中的 UI(高效更新)。
若是有人要求你給出一個 React 爲何值得學習的緣由,就是它是一個基於結果的 UI 語言。咱們能夠將這種語言稱爲 React語言 。
假設咱們有一個像這樣的 todos
列表:
const todos: [ { body: 'Learn React Fundamentals', done: true }, { body: 'Build a TODOs App', done: false }, { body: 'Build a Game', done: false }, ];
此 todos
數組是 UI 的起始狀態。你須要構建一個 UI 來顯示和管理。在這個頁面上有三個操做,風別是一個添加新 todo
的表單 ,一個將 todo
標記爲已完成,以及刪除全部已完成的 todo
。
這些操做中的每個都將要求應用程序執行DOM操做以建立,插入,更新或刪除 DOM 節點。使用 React ,你沒必要擔憂全部這些 DOM 操做。你沒必要擔憂什麼時候須要發生或如何有效地執行它們。你只需將 todos
數組置於應用程序的 state
中,而後使用 React 語言命令 React 在 UI 中以某種方式顯示該狀態:
<header>TODO List</header> <ul> {todos.map(todo => <li>{todo.body}</li> )} </ul> // Other form elements...
以後,你能夠專一於對該todos
數組進行數據操做!你能夠添加,刪除和更新該數組,React 會將你對該對象所作的更改渲染到瀏覽器上。
這種基於最終狀態建模 UI 的心理模型更易於理解和使用,尤爲是當視圖具備大量數據轉換時。例如,考慮一下能夠告訴你有多少朋友在線的視圖。該視圖的 state
只是目前有多少朋友在線的一個數字。它並不關心剛纔三個朋友上網,而後其中一個斷線,而後兩個加入。它只知道在這個時刻,有四個朋友在線。
在 React 以前,當咱們須要使用瀏覽器的API(DOM API)時,咱們儘量避免遍歷 DOM 樹,那是由於 DOM 上的任何操做都在同一個線程中完成,該線程負責瀏覽器中發生的全部事情,包括對用戶事件的反應:如打字,滾動,調整大小等。
對 DOM 的任何昂貴的操做均可能給用戶帶來緩慢的操做體驗。很是重要的是,你的應用程序執行最小的操做時,應儘量地批量處理。React 就提出了一個獨特的概念來幫助咱們作到這一點!
當咱們告訴 React 在瀏覽器中渲染元素樹時,它首先生成該樹的虛擬表示並將其保存在內存中以供往後使用。而後它將繼續執行DOM操做,使樹顯示在瀏覽器中。
當咱們告訴 React 更新以前渲染的元素樹時,它會生成樹的新的虛擬表示。如今React在內存中有2個版本的樹!
要在瀏覽器中呈現更新的樹,React 不會丟棄已呈現的內容。相反,它將比較它在內存中的2個虛擬版本,計算它們之間的差別,找出主樹中須要更新的子樹,而且只在瀏覽器中更新這些子樹。
這個過程就是所謂的樹協調算法,它是 React 渲染瀏覽器 DOM 樹的一種很是有效的方法。
除了基於聲明結果的語言和有效的樹協調以外,如下是我認爲React得到其普遍流行的其餘一些緣由:
爲了看到樹協調算法的實際好處及其所帶來的巨大差別,讓咱們看一個只關注該概念的簡單示例。讓咱們生成並更新兩次HTML元素樹,一次使用本機Web API,而後使用React API(及其協調工做)。爲了簡化這個例子,我不會使用組件或 JSX(與React一塊兒使用的 JavaScript 擴展)。我還將在 JavaScript 間隔計時器內執行更新操做。這不是咱們編寫React應用程序的方式,而是讓咱們一次關注一個概念。
在此會話中,使用2種方法將簡單的HTML元素呈現給顯示:
方法1:直接使用 Web DOM API
document.getElementById('mountNode').innerHTML = ` <div> Hello HTML </div> `;
方法2:使用 React API
ReactDOM.render( React.createElement( 'div', null, 'Hello React', ), document.getElementById('mountNode2'), );
該 ReactDOM.render
方法和 React.createElement
方法是 React 應用程序中的核心 API 方法。事實上,若是不使用這兩種方法,React Web 應用程序就不可能存在。簡要介紹一下:
是 React 應用程序渲染到瀏覽器 DOM 的入口點。它有兩個參數:
mountNode2
元素,該元素存在於playground 的顯示區域中(第一個 mountNode
用於本機版本)。React元素到底是什麼?它是用來描述 Actual DOM 元素的 Virtual 元素。也就是 React.createElement
API方法返回的內容。
在 React 中,咱們不使用字符串來表示 DOM 元素(如上面的 DOM 示例中),而是使用對方法的調用來表示帶有對象的 DOM 元素 React.createElement
。這些對象稱爲 React 元素。
該 React.createElement
函數有不少參數:
div
在此示例中。id
,href
,title
,等),若是沒有屬性,可使用 null
。Hello React
字符串。可選的第三個參數以及它後面的全部可選參數,造成渲染元素的子列表。元素能夠包含0個或更多子元素。React.createElement
也可用於從 React 組件建立元素。
React 元素在內存中建立。爲了實際在真實 DOM 中顯示一個 React 元素,咱們使用 ReactDOM.render
來實現將 React 元素的狀態映射到瀏覽器中的真實 DOM 樹中。
咱們有兩個節點:一個用 DOM API 直接控制,另外一個用 React API 控制。
咱們在瀏覽器中構建這兩個節點的方式之間惟一的區別是,在 HTML 版本中,咱們使用字符串來表示 DOM 樹,而在 React 版本中,咱們使用純 JavaScript 調用並使用對象表示 DOM 樹。
不管 HTML UI 有多複雜,使用 React 時,每一個 HTML 元素都將用 React 元素表示。
對於 HTML 版本,你能夠直接在模板中注入新元素的標記:
document.getElementById('mountNode').innerHTML = ` <div> Hello HTML <input /> </div> `;
而對 React 執行相同操做,就須要在 React.createElement
上面的第三個參數以後添加更多參數。爲了匹配到在原生 DOM 示例中的內容,咱們能夠添加第四個參數,這是另外一個 React.createElement
呈現 input
元素的調用:
ReactDOM.render( React.createElement( "div", null, "Hello React ", React.createElement("input") ), document.getElementById('mountNode2'), );
可使用它 new Date().toLocaleTimeString()
來顯示簡單的時間字符串,並把它放在一個 pre
標籤中。
原生 DOM 版本執行的操做:
document.getElementById('mountNode1').innerHTML = ` <div> Hello HTML <input /> <pre>${new Date().toLocaleTimeString()}</pre> </div> `;
在 React 中,咱們須要在頂層 div
元素中添加第五個參數。而且,這個新的第五個參數是另外一個 React.createElement
調用,建立一個 pre
標籤,而且內容爲 Date().toLocaleTimeString()
:
ReactDOM.render( React.createElement( 'div', null, 'Hello React ', React.createElement('input'), React.createElement( 'pre', null, new Date().toLocaleTimeString() ) ), document.getElementById('mountNode2') );
所以,你可能認爲使用 React 比使用簡單熟悉的原生方式要困可貴多。那麼爲何咱們要放棄熟悉的 HTML 而且必須學習 React API 來編寫能夠用 HTML 編寫實現的內容喃?
答案不在於渲染第一個 HTML 視圖,這是在於咱們如何更新已渲染的 DOM 視圖。
咱們對 DOM 樹進行更新操做。例如:簡單地讓時間字符串每秒更新。
咱們可使用 setInterval
Web 計時器 API 輕鬆地在瀏覽器中重複 JavaScript 函數調用。將兩個版本的全部 DOM 操做放入一個函數中,命名它 render
,並在 setInterval
調用中使用它以使其每秒重複一次。
如下是此示例的完整代碼:
const render = () => { // HTML DOM document.getElementById('mountNode').innerHTML = ` <div> Hello HTML <input /> <pre>${new Date().toLocaleTimeString()}</pre> </div> `; // React DOM ReactDOM.render( React.createElement( 'div', null, 'Hello React', React.createElement('input', null), React.createElement('pre', null, new Date().toLocaleTimeString()) ), document.getElementById('mountNode2') ); }; // 每秒更新一次 setInterval(render, 1000);
請注意兩個版本中的時間字符串如何每秒更新。咱們如今正在更新 DOM 中的 UI 。
這是React可能會讓你大吃一驚的時刻。若是你嘗試在原生 DOM 版本的文本框中鍵入內容,則沒法執行此操做。這是由於咱們每秒都會拋棄原有的整個DOM節點並從新生成它。
可是,若是你嘗試在 React 版本中的文本框中鍵入內容,卻能夠執行。
雖然整個 React 渲染代碼都在計時器內,但 React只更改 pre
元素的內容而不是整個 DOM 樹。這就是文本輸入框沒有從新生成的緣由,咱們能夠輸入它。
若是你檢查 Chrome DevTools 元素面板中的兩個DOM節點,你能夠看到這兩種方式更新 DOM 的不一樣。
div#mountNode
每秒從新生成其整個 DOM 樹div#mountNode2
容器中僅 pre
每秒從新生成
這是 React 的智能差別算法。它只在主 DOM 樹中更新實際須要更新的內容,同時保持其餘全部內容相同。這種差別化過程是可行的,由於它在內存中保留了 React 的虛擬 DOM 表示。不管 UI 視圖須要從新生成多少次, React 將只向瀏覽器提供所需的更新部分。
這種方法不只效率更高,並且消除了咱們考慮更新 UI 的方式的複雜性。讓 React 處理關因而否須要更新 DOM 的全部計算模塊,使咱們可以專一于思考咱們的數據(state
狀態)以及 UI 展現。
而後,咱們根據須要管理數據狀態的更新,而沒必要擔憂在瀏覽器的實際 UI 中渲染這些更新所需的步驟(由於咱們知道 React 將徹底執行此操做而且以更有效的方式執行!)
本文翻譯自:https://jscomplete.com/learn/...
走在最後,歡迎關注:前端瓶子君