去年,我寫了一本關於學習React.js的小書,原來是大約100頁。 今年我要挑戰本身,把它概括爲一篇文章。react
本文不會涵蓋什麼是React,或者爲何要學習它。 相反,這是面向已經熟悉JavaScript並熟悉DOM API基礎知識的人,對React.js的基礎知識的介紹。程序員
如下全部代碼示例均標示爲參考。 它們純粹是爲了提供概念而寫的例子。 他們大多數能夠寫得更好一些。express
React是圍繞可重用組件的概念設計的。 您定義小組件,並將它們放在一塊兒造成更大的組件。數組
全部小或小的組件均可重複使用,甚至跨不一樣的項目。瀏覽器
一個React組件(以其最簡單的形式)是一個簡單的JavaScript函數:安全
// Example 1 // https://jscomplete.com/repl?j=Sy3QAdKHW function Button (props) { // Returns a DOM element here. For example: return <button type="submit">{props.label}</button>; } // To render the Button component to the browser ReactDOM.render(<Button label="Save" />, mountNode)
用於按鈕標籤的花括號將在下面介紹。 如今沒必要要擔憂他們。 ReactDOM也將在後面解釋,可是若是要測試這個例子和接下來的代碼示例,render
函數就是你須要的。微信
ReactDOM.render
的第二個參數是React將要接管和控制的目標DOM元素。 在jsComplete REPL中,您就可使用mountNode
變量。架構
關於示例1的注意事項有如下幾點:app
props
。建立功能組件,你能夠經過使用任意名稱命名props
。上面的示例1能夠用純粹的React.js來編寫,而不須要JSX,以下所示:dom
// Example 2 - React component without JSX // https://jscomplete.com/repl?j=HyiEwoYB- function Button (props) { return React.createElement( "button", { type: "submit" }, props.label ); } // To use Button, you would do something like ReactDOM.render( React.createElement(Button, { label: "Save" }), mountNode );
createElement
函數是React頂級API中函數。 您須要學習的這個級別中共有7件事情中的1項。 可見ReactApi多麼簡短。
很像DOM自己有一個document.createElement
函數來建立一個由標籤名稱指定的元素,React的createElement
函數是一個更高級別的函數,能夠作相似於document.createElement
的功能。 但它也能夠用於建立一個表示React組件的元素。 當咱們使用上面的例2中的Button組件時,咱們這裏就是建立了一個React組件。
與document.createElement
不一樣,React的createElement
能夠接受第二個參數以後的動態參數,以表示建立的元素的後代。 因此createElement
實際上建立一個樹。
這是一個例子:
/ Example 3 - React’s createElement API // https://jscomplete.com/repl?j=r1GNoiFBb const InputForm = React.createElement( "form", { target: "_blank", action: "https://google.com/search" }, React.createElement("div", null, "Enter input and click Search"), React.createElement("input", { name: "q", className: "input" }), React.createElement(Button, { label: "Search" }) ); // InputForm uses the Button component, so we need that too: function Button (props) { return React.createElement( "button", { type: "submit" }, props.label ); } // Then we can use InputForm directly with .render ReactDOM.render(InputForm, mountNode);
關於以上例子要注意的幾點:
InputForm
不是React組件; 它只是一個React元素。 這就是爲何咱們直接在ReactDOM.render
調用中使用它,而不是使用<InputForm />
。React.createElement
調用,由於它都是JavaScript。React.createElement
的第二個參數能夠是null,也能夠是一個空對象,當元素不須要attributes和props時。上面的代碼是您在引入React庫時瞭解的內容。 瀏覽器不處理任何JSX業務。 然而,咱們人類喜歡看HTML而且使用HTML而不是這些createElement
調用(想象一下使用document.createElement
構建一個網站,我相信你能夠的!)。 這就是爲何存在JSX的緣由。 咱們能夠用很是相似於HTML的語法編寫它,而不是用React.createElement
調用上面的表單:
// Example 4 - JSX (compare with Example 3) // https://jscomplete.com/repl?j=SJWy3otHW const InputForm = <form target="_blank" action="https://google.com/search"> <div>Enter input and click Search</div> <input name="q" className="input" /> <Button label="Search" /> </form>; // InputForm "still" uses the Button component, so we need that too. // Either JSX or normal form would do function Button (props) { // Returns a DOM element here. For example: return <button type="submit">{props.label}</button>; } // Then we can use InputForm directly with .render ReactDOM.render(InputForm, mountNode);
關於上面的例子注意如下幾點
咱們上面寫的(例4)是JSX。 然而,咱們在瀏覽器的執行版本是它的編譯版本(示例3)。 爲了實現這一點,咱們須要使用預處理器將JSX版本轉換爲React.createElement
版本。
那就是JSX。 這是一個折中,容許咱們以相似於HTML的語法編寫咱們的React組件,這是一個很好的共識。
上面標題中的「Flux」一詞被選爲韻腳(...),但它也是Facebook流行的很是受歡迎的應用程序架構的名稱。 最着名的實現是Redux。
JSX,順便說一下,能夠本身在其餘地方使用。 這不是隻有在React中才可使用的。
在JSX部分中,您能夠在一對花括號內使用任何JavaScript表達式。
// Example 5 - Using JavaScript expressions in JSX // https://jscomplete.com/repl?j=SkNN3oYSW const RandomValue = () => <div> { Math.floor(Math.random() * 100) } </div>; // To use it: ReactDOM.render(<RandomValue />, mountNode);
任何JavaScript表達式均可以放在那些花括號內。 這至關於JavaScript模板文字中的$ {}
插值語法。
這是JSX中惟一的約束:只有表達式。 因此,你不能使用常規的if語句,可是三元表達式是能夠的。
JavaScript變量也是表達式,因此當組件接收到props
列表(RandomValue組件沒有,props
是可選的)時,能夠在花括號內使用這些props
。 咱們在上面的Button組件中這樣作了(示例1)。
JavaScript對象也是表達式。 有時候,咱們在一個花括號裏面使用一個JavaScript對象,這使得它看起來像雙花括號,但它實際上只是一個大括號內的一個對象。 一個用例是將CSS樣式對象傳遞給React中的style屬性:
// Example 6 - An object passed to the special React style prop // https://jscomplete.com/repl?j=S1Kw2sFHb const ErrorDisplay = ({message}) => <div style={ { color: 'red', backgroundColor: 'yellow' } }> {message} </div>; // Use it: ReactDOM.render( <ErrorDisplay message="These aren't the droids you're looking for" />, mountNode );
請注意,我如何僅解析props
參數中的message的。 這是JavaScript。 還要注意上面的style屬性是一個特殊的屬性(再次,它不是HTML,它更接近於DOM API)。 咱們使用一個對象做爲style屬性的值。 該對象定義了樣式,就像咱們使用JavaScript同樣(由於確實就是)。
甚至能夠在JSX中使用React元素,由於這也是一個表達式。 記住,一個React元素就是一個函數調用:
const MaybeError = ({errorMessage}) => <div> {errorMessage && <ErrorDisplay message={errorMessage} />} </div>; // The MaybeError component uses the ErrorDisplay component: const ErrorDisplay = ({message}) => <div style={ { color: 'red', backgroundColor: 'yellow' } }> {message} </div>; // Now we can use the MaybeError component: ReactDOM.render( <MaybeError errorMessage={Math.random() > 0.5 ? 'Not good' : ''} />, mountNode );
上面的MaybeError
組件將只顯示ErrorDisplay
組件,若是有一個errorMessage
字符串傳遞給它和一個空的div。 React將{true}
,{false}
,{undefined}
和{null}
視爲沒有呈現任何內容的有效元素子元素。
您還可使用JSX內的集合上的全部JavaScript方法(map,reduce,filter,concat等)。 再次聲明緣由是由於它們返回的是表達式:
// Example 8 - Using an array map inside {} // https://jscomplete.com/repl?j=SJ29aiYH- const Doubler = ({value=[1, 2, 3]}) => <div> {value.map(e => e * 2)} </div>; // Use it ReactDOM.render(<Doubler />, mountNode);
請注意,我是如何給value
props默認值的,由於它全是Javascript。 還要注意,我在div中輸出了一個數組表達式,這在React中是可行的。 它將把每個雙倍的值放在一個文本節點中。
簡單的功能組件很是適合簡單的需求,但有時咱們須要更多的功能。 React支持經過JavaScript類語法建立組件。 這是使用類語法編寫的Button組件(在示例1中):
// Example 9 - Creating components using JavaScript classes // https://jscomplete.com/repl?j=ryjk0iKHb class Button extends React.Component { render() { return <button>{this.props.label}</button>; } } // Use it (same syntax) ReactDOM.render(<Button label="Save" />, mountNode);
類語法很簡單。 定義一個擴展了React.Component基類的類(須要學習的另外一個頂級的React API)。 該類定義一個惟一實例函數render(),該render函數返回虛擬DOM對象。 每次咱們使用上面的基於Button類的組件(例如,經過執行<Button ... />),React將從這個基於類的組件中實例化一個對象,並在DOM樹中使用該對象。
這就是爲何咱們在上面的渲染輸出中在JSX中使用this.props.label
的緣由。 由於每一個組件都得到一個稱爲props
的特殊實例屬性,該實例屬性在實例化時保存傳遞給該組件的全部值。
// Example 10 - Customizing a component instance // https://jscomplete.com/repl?j=rko7RsKS- class Button extends React.Component { constructor(props) { super(props); this.id = Date.now(); } render() { return <button id={this.id}>{this.props.label}</button>; } } // Use it ReactDOM.render(<Button label="Save" />, mountNode);
咱們還能夠定義類屬性函數,並在咱們想使用的地方使用,包括返回的JSX輸出內:
// Example 11 — Using class properties // https://jscomplete.com/repl?j=H1YDCoFSb class Button extends React.Component { clickCounter = 0; handleClick = () => { console.log(`Clicked: ${++this.clickCounter}`); }; render() { return ( <button id={this.id} onClick={this.handleClick}> {this.props.label} </button> ); } } // Use it ReactDOM.render(<Button label="Save" />, mountNode);
關於例子11有幾點須要注意
handleClick
函數是使用JavaScript中新建的類字段語法編寫的。這種語法仍然屬於stage-2,
,但因爲不少緣由,它是訪問組件安裝實例(因爲箭頭功能)的最佳選擇。 可是,您須要使用像Babel這樣的編譯器來配置它來理解stage-2
,(或類字段語法)來獲取上面的代碼。 jsComplete REPL具備預配置。ClickCounter
實例變量。 這容許咱們徹底跳過使用類構造函數調用。handleClick
函數指定爲特殊的onClick
,React屬性的值時,咱們沒有調用它。 咱們把handleClick函數引用傳遞給出去了。 在這個屬性裏面調用函數是使用React最多見的錯誤之一。// Wrong: onClick={this.handleClick()} // Right: onClick={this.handleClick}
在React元素中處理事件時,與DOM API的方式有兩個很是重要的區別:
onClick
,而不是onclick
。onClick = {handleClick}
,而不是onClick =「handleClick」
。使用本身的對象將DOM事件對象包裝起來,以優化事件處理的性能。 可是在事件處理程序中,咱們仍然能夠訪問DOM事件對象上可用的全部方法。 React將包裝的事件對象傳遞給每一個句柄調用。 例如,爲了防止表單從默認提交操做中,您能夠執行如下操做:
// Example 12 - Working with wrapped events // https://jscomplete.com/repl?j=HkIhRoKBb class Form extends React.Component { handleSubmit = (event) => { event.preventDefault(); console.log('Form submitted'); }; render() { return ( <form onSubmit={this.handleSubmit}> <button type="submit">Submit</button> </form> ); } } // Use it ReactDOM.render(<Form />, mountNode);
如下僅適用於類組件(擴展爲React.Component的組件)。 函數組件有一個略有不一樣的故事。
this.props
訪問的props
。 那些props
正是咱們在上面的步驟2中傳遞的。render
方法(虛擬DOM節點)的輸出。componentDidMount
。 咱們可使用這種方法作一些事情,例如,在DOM上作一些咱們如今知道在瀏覽器中支持處理的東西。 在今生命週期方法以前,咱們處理的DOM所有是虛擬的。componentWillUnmount
。props
。 這裏的魔法發生了,咱們如今開始須要React了! 在此以前,咱們徹底不須要作任何事情如下也僅適用於類組件。 有沒有人提到有些人把只作展示的組件叫作啞吧?
狀態類字段是任何React類組件中的特殊字段。 React監視每一個組件狀態以進行更改。 可是對於React要有效地執行這些操做,咱們必須經過另外一個須要學習的React API函數來更改state字段,this.setState
:
// Example 13 - the setState API // https://jscomplete.com/repl?j=H1fek2KH- class CounterButton extends React.Component { state = { clickCounter: 0, currentTimestamp: new Date(), }; handleClick = () => { this.setState((prevState) => { return { clickCounter: prevState.clickCounter + 1 }; }); }; componentDidMount() { setInterval(() => { this.setState({ currentTimestamp: new Date() }) }, 1000); } render() { return ( <div> <button onClick={this.handleClick}>Click</button> <p>Clicked: {this.state.clickCounter}</p> <p>Time: {this.state.currentTimestamp.toLocaleString()}</p> </div> ); } } // Use it ReactDOM.render(<CounterButton />, mountNode);
這是瞭解state
最重要的例子。 它將完善您對React交互方式的基礎知識。 在這個例子以後,還有一些你須要學習的小事情,可是從這一點來看,它主要是你和你的JavaScript技能。
咱們來看一下實例13,從類字段開始。 它有兩個。 特殊狀態字段被初始化爲一個對象,該對象包含起始值爲0的clickCounter
,以及起始值爲new Date()
的currentTimestamp
。
第二個類字段是一個handleClick
函數,咱們傳遞給render方法中的button元素的onClick
事件。 handleClick
方法使用setState
修改此組件實例狀態。 注意到這一點。
咱們在componentDidMount
生命週期方法內部啓動的間隔定時器中修改狀態。 它每秒鐘打勾並執行調用this.setState
。
在render方法中,咱們使用了正常讀取語法對state兩個屬性的讀取。 沒有特殊的API。
如今,請注意,咱們使用兩種不一樣的方式更新了狀態:
handleClick
函數中實現了這部份內容。這兩種方式都是能夠接受的,可是當您同時讀取和寫入狀態時,第一個是首選的(咱們這樣作)。 在間隔回調以內,咱們只寫給狀態,而不是讀取它。 當兩難時,始終使用第一個函數參數語法。 它更加安全,由於setState
其實是一個異步方法。
咱們如何更新狀態? 咱們返回一個包含咱們要更新的值的對象。 注意在兩次調用setState
中,咱們只是從state
字段傳遞一個屬性,而不是二者。 這是徹底能夠的,由於setState
實際上將您傳遞的內容(函數參數的返回值)與現有狀態合併。 所以,在調用setState
時不指定屬性意味着咱們不但願更改該屬性(而不是刪除它)。
React從它對狀態變化作出響應的事實(雖然不是反應性的,而是按計劃進行)而得名。 有一個笑話,反應應該被命名爲Schedule!
然而,當任何組件的狀態被更新時,咱們用肉眼看到的是React對該更新作出反應,並自動反映瀏覽器DOM中的更新(若是須要)。
將render函數輸入視爲二者
props
當渲染功能的輸入變化時,其輸出可能會改變。
React保留了渲染歷史的記錄,當它看到一個渲染與前一個渲染不一樣時,它將計算它們之間的差別,並將其有效地轉換爲在DOM中執行的實際DOM操做。
您能夠將React視爲咱們聘請的與瀏覽器通訊的代理。 以上面的當前時間戳顯示爲例。 咱們不是手動去瀏覽器並調用DOM API操做來每秒查找和更新p#timestamp元素,而是在組件狀態上更改了一個屬性,而React表明咱們與瀏覽器進行通訊。 我相信這是真正受歡迎的真正緣由。 咱們討厭瀏覽器(domApi很繁瑣),React自願爲咱們作全部對接工做,免費!
如今咱們知道一個組件的狀態,以及當這個狀態改變了一些魔法的時候,讓咱們來學習關於該過程的最後幾個概念。
props
時,該組件可能須要從新呈現componentWillReceiveProps
。props
被更改,則React有一個重要的決定。 組件應該在DOM中更新嗎? 這就是爲何它在這裏調用另外一個重要的生命週期方法,shouldComponentUpdate
。 這個方法是一個實際的問題,因此若是你須要本身定製或優化渲染過程,你必須經過返回true或false來回答這個問題。customComponentUpdate
,React默認是一個很是聰明的事情,在大多數狀況下實際上足夠好。componentWillUpdate
。 而後React將計算新的渲染輸出並將其與最後渲染的輸出進行比較。componentDidUpdate
。生命週期方法其實是艙口。 若是你沒有作任何事情,你能夠建立沒有他們的完整的應用程序。 他們能夠用來很是方便地分析應用程序中發生的狀況,並進一步優化了React更新的性能。
根據以上學到的東西(或其中的一部分,真的),您就能夠開始建立一些有趣的React應用程序。 若是您渴望瞭解更多信息,請訪問咱們的Plactsight的React.js課程入門:
翻譯自All the fundamental React.js concepts, jammed into this single Medium article
關注個人公衆號,更多優質文章定時推送
建立了一個程序員交流微信羣,你們進羣交流IT技術
若是已過時,能夠添加博主微信號15706211347,拉你進羣