React其實很好上手,我在最初使用時並未去了解其一些細節性的東西,可是好像在項目中也一直能正常運做。可是那時總會有一種不安感,深感本身對React的使用邏輯並未理解得很是清晰,本文的目的就在於理清這種使用邏輯,固然我的看法定有偏頗,若是你有一些建議,也但願您能在討論區予以指教,若是你到如今尚未怎麼接觸過React,推薦能夠跟着官方文檔的例子體會下React再來看本文,也許這樣收穫更大一些,後文的連接裏還有一個更加高級的例子也是很是好的入門教程。html
這是一個老生常談的問題了,可能你們在衆多的教程、文章裏已經瞭解過了React的好處,好比說它的虛擬DOM能夠被高效的渲染,好比說它的組件化使得項目結構很是清晰,代碼複用很是容易,好比說它的數據管理機制也能讓你清晰的知曉數據的狀態,而React自己就是被這種清晰的數據所驅動的。前端
"We built React to solve one problem: building large applications with data that changes over time."node
詳細談論這些優勢前,我想說說React給我帶來的改變。react
在使用React以前,我也一直在使用jQuery,它對節點的操做很是方便,若是僅僅只是普通網頁的開發,jQuery無疑是很是好的,可是若是開發的是WebApp,jQuery並不能加強你對全局的把控能力,在學習使用React沒多久之後忽然有一天我感受之前所作的開發都好像在玩一些小打小鬧的遊戲,而使用React也讓本身明白了爲何我被稱做前端工程師,這個框架讓我找到了工程師的歸屬感,當我再看本身的項目時和一個建築工程師看本身的設計圖的感受沒了太多差異,我知道個人這個組件是房子的整體框架,這個組件是大廳,這個組件是椅子,還有與大廳同級的臥室組件,廚房組件,與椅子同級的桌子組件,家電組件。webpack
我能夠用優雅的桌子,椅子,牀,檯燈來佈置一個舒適的臥室;
我能夠把桌子壓扁拉長變成電視櫃,把椅子拉寬加上軟軟的海綿變成沙發,再把檯燈提升,換一個像樣的遮光罩它就是落地燈了,再加上一些客廳獨有的家電,客廳的感受也就出來了;
用相同的思路,一個舒適的家就出來了。git
其實想一想,若是隻考慮客廳和臥室(不考慮裏面的那些桌子椅子之類的組件)那麼除卻它們的長、寬、擺放位置這些參數不一樣,它們又有什麼區別呢?github
回到React,它即帶給咱們對總體的把控能力,也讓咱們能夠經過修改數據(參數)以表現不一樣的細節達到不一樣的效果,從最大的房子的框架到每一個桌子椅子的樣子,一切都在咱們的掌握。下面就慢慢說說React是如何幫咱們達到把握全局,瞭解細節的。web
想象這麼一個場景,客廳裏有一把咱們不是很喜歡的椅子,想換一把,最合適的作法固然就是改造一下,或者把這把丟了從新買一把新的,爲了換一把椅子而從新組裝整個房子一看就是不聰明的作法。Virtual DOM爲咱們提供了一種高效的渲染機制,使得咱們能夠只改變咱們想改變的地方,而儘可能不去影響其它無關的組件。它是React高性能的基礎。算法
要說明Virtual DOM到底是什麼,不得不提到React DOM模塊的一個方法,ReactDOM.reader()
。npm
這個方法就像打開一道門的鑰匙,門的兩邊就是Virtual DOM和Html DOM,咱們在瀏覽器中看到的確定是Html DOM,Virtual DOM存在於隔着這道門的系統內存之中,Html DOM和Virtual DOM之間存在着映射關係。
就像Html DOM由各類節點構成,Virtual DOM也是由一種被稱爲React node
的節點構成。
每一個React組件中還有另一個render()
方法(不一樣於ReactDOM.reader()
),個人理解是這個方法用於將ReactNode
構建爲Virtual DOM。下面再來詳細看看React node
。
「a light, stateless, immutable, virtual representation of a DOM node.」
React node其實並不是真實的節點,實際上它們能夠看作是真實節點在Virtual DOM中的表明,Virtual DOM就是由ReactNodes構成,真實的DOM就是依據它們所構建;
在須要改變真實的DOM時,React實際上是先修改虛擬DOM,而後和真實的DOM作比較,在真實DOM中只改變須要改變的地方,這種補丁機制只改變局部,不改變總體,所以對系統性能的消耗較小,對虛擬DOM的修改會在狀態改變時觸發,後文會詳細說明這種狀態機制。可能你們也已經據說了兩者之間的比較是基於diff算法,知乎上有一篇詳細解析React的這個算法的文章,推薦你們閱讀。
通常來講建立React node有兩種方法,以下
// 方法1,使用React 內置的工廠方法建立 var reactNodeLi = React.DOM.li({id:'li1'}, 'one'); //方法2,使用JavaScript建立node的方法 var reactNodeLi = React.createElement('li', null, 'one');
最近有一本開源的電子書React Enlightenment裏有一章對React node有詳細的介紹,也推薦你們閱讀。
React提供的另一種簡潔,直觀的建立React node的方法,那就是JSX,其實提到React,你們好像都會想到JSX,由於它實在是太方便了,其實使用React其實並不是必須使用JSX,不過使用它真的能讓咱們的工做更加輕鬆。
var App = React.createClass({ render: function() { return <p>My name is { this.props.name }</p>; } });
上面例子裏return中的那一部分就是JSX了,初看JSX的語法,可能你們會想到前端開發中常用到的模板,不過JSX並不是模板,它應該算是React對JS語法的拓展,須要編譯後才能正確使用它,JSX的構建是很是簡潔明瞭的,在此就再也不贅述。
剛剛已經說起JSX是須要編譯才能被瀏覽器識別的,它就是被Babel編譯的,具體說來是被babel-preset-react來編譯的。不過Babel的最主要目的其實並不是編譯JSX,Babel應該算是一個編譯平臺,其主要目的是轉換你在代碼中使用了的ES6甚至ES7語法爲瀏覽器識別的ES5語法(babel-core,babel-preset-es2015模塊),編譯React倒像是其的附加功能。初學者有時候會以爲使用React困難,配置合適的開發環境但是就是緣由之一。之前翻譯過一篇基礎的配置webpack的文章,具體能夠點這裏。
除卻高性能,組件是另一個React很是吸引人的地方,組件的可複用性,可組合性以及其對模塊化開發的自然適應性,使得咱們的項目很是直觀,便於理解和管理。拿到一個項目,最開始要想的就是如何來劃分組件。固然劃分確定須要一些依據,先來看看React本身對組件的分類。
我在最初使用React時,個人項目裏的全部的組件都是經過React.createClass()
建立,全部的組件在裏面可能都擁有getInitialState(),componentDidMount()
等方法,固然這樣用其實一點也沒有問題。可是這樣寫,除非對項目很是熟悉,不然咱們並不能很容易的就區分組件之間的層級關係。並且隨着項目的複雜化,也不利於數據的管理。
以前在使用React重構百度新聞webapp前端看到智能組件和木偶組件二詞,我以爲它們可能能夠分別對應到Stateful Component
和Stateless Component
,在此引用一下該文裏的說法。
智能組件 它是數據的全部者,它擁有數據、且擁有操做數據的action,可是它不實現任何具體功能。它會將數據和操做action傳遞給子組件,讓子組件來完成UI或者功能。這就是智能組件,也就是項目中的各個頁面。
這是一個完整的組件,在這種組件裏可能會出現全部的React提供的方法(包括各類生命週期函數life cycle methods
,各類事件響應函數等等)
建立:
//ES5 寫法 var App = React.createClass({ getInitialState(){ return{ name:"Tom", ... } }, componentDidMount(){ this.setState({ name:"Jim" }) }, render: function() { return <p>My name is { this.state.name }</p>; } }); // ES6 寫法 class SearchBar extends React.Component { constructor(props) {//props須要做爲參數傳入 super(props);//須要使用super,若是沒有this就會是undefined this.state = { searchTerm: '', }; this.handleInputChange = this.handleInputChange.bind(this);//爲事件綁定this,這是ES6語法所要求的,ES5並沒相關要求 } handleInputChange(event) { this.setState({ searchTerm: event.target.value, }); } render() { return <input onChange={this.handleInputChange} />; } }
兩種寫法其實沒有本質區別,ES6語法也會經過Babel轉換爲ES5語法後被執行,可是兩種寫法裏確實存在一些不同的地方,好比說使用ES6時須要單獨綁定this
,ES6語法裏方法之間不能使用逗號,
等等。網上能夠查到不少相關資料,在此不作贅述。
木偶組件:它就是一個工具,不擁有任何數據、及操做數據的action,給它什麼數據它就顯示什麼數據,給它什麼方法,它就調用什麼方法,比較傻。這就是木偶組件,即項目中的各個組件。
這種組件裏只會出現,React提供的render()
方法,用於構建虛擬DOM,其建立方式除了ES5,ES6的寫法,還可使用Stateless Functions
方法建立。
建立:
//ES5 var HelloMessage = React.createClass({ render(){ return <div>Hello {props.name}</div> //多個節點時須要加括號 } }) //ES6 class HelloMessage extends React.Component { constructor(props) {//props須要做爲參數傳入 super(props);//須要使用super,若是沒有this就會是undefined } render() { return <div>Hello {props.name}</div>; } } //Stateless Functions function HelloMessage(props) { return <div>Hello {props.name}</div>; } //ES6 Stateless Functions const HelloMessage = (props) => <div>Hello {props.name}</div>;
如若須要,全部的React組件都是能夠當作模塊被導出的,不過就就我本人看來,通常所導出的模塊都是由一個或者若干個組件組成的功能單元。不過說到這裏更想說明的一點時,React實際上是很依賴相似於webpack這樣的模塊管理工具的,因此想要用好React,其實也須要對模塊的定義,以及模塊管理工具備一點的瞭解。
React裏的組件是活的,組件不只僅有相似於出生,成長,死亡的過程,還有心臟和血液。
組件的生命週期函數能夠分爲三個階段:
Mounting Phase(此階段的函數在一個組件的生命中只會執行一次)(掛載階段)
- getInitialState() - componentWillMount() - componentDidMount()
Updating Phase(此階段的函數在一個組件的生命中可別屢次執行)(更新階段)
- componentWillReceiveProps() - shouldComponentUpdate() - componentWillUpdate() - componentDidUpdate()
Unmount Phase (此階段的函數在一個組件的生命中只會執行一次)(卸載階段)
- componentWillUnmount()
關於各個函數的具體意義,在此不在贅述,一個比較容易出錯的地方是弄明白各個函數的執行順序,下面給出一個參考列表。
- Mounting Phase: 1. Initialize / Construction 2. getDefaultProps() (React.createClass) or MyComponent.defaultProps (ES6 class) 3. getInitialState() (React.createClass) or this.state = ... (ES6 constructor) 4. componentWillMount() 5. render() 6. Children initialization & life cycle kickoff 7. componentDidMount() - Updating Phase follows this order: 1. componentWillReceiveProps() 2. shouldComponentUpdate() 3. render() 4. Children Life cycle methods 5. componentWillUpdate() - Unmount Phase follows this order: 1. componentWillUnmount() 2. Children Life cycle methods 3. Instance destroyed for Garbage Collection
用過React的人都知道,this.setState({})
可能算是React裏使用最多的方法了,每次使用都會根據所更新的數據重構Virtual DOM已達到更新組件的目的,使得組件充滿活力,知足咱們的各類要求。
state在getInitialState()階段被初始化,以後經過其它生命週期函數(componentWillUpdate()
裏不能使用)或React事件調用的函數,可使用利用this.setState({})
更新某一state的值。
我在最初使用React時總以爲,使用了過多的this.setState({})
會不會致使React變得性能低下,不過閱讀了這篇文章打消了個人一些疑惑。
爲何把props
比做血液呢,由於它自己本身並不會變化,它就像是一個傳輸的中介,把父組件的方法,屬性傳遞給子組件。通常在子組件中它可能有三方面的做用
做爲子組件的屬性<div className="this.props.className">做爲屬性</div>
;
做爲參數<div>{"個人名字是"+this.props.name}</div>
;
傳遞方法<div onClick="this.props.click">傳遞方法</div>
;
配合state
,props能夠用來改變子組件的表現形式,若是用來傳遞方法,props
能夠在子組件中調用父組件的方法。
在開發時,props還能夠配合propTypes
使用,這樣可使得props的使用更加準確(以下例),也使得組件更加健壯。
const AlbumList = (props) => { const albums = props.albums.map((album) => <li>{album.name}</li>); return ( <ul> {albums} </ul> ); }; AlbumList.propTypes = { albums: React.PropTypes.array.isRequired, };
本文只總結了我對React的最基礎的部分的一些思考,相似於高階組件,Redux這類的目前我並未接觸過多的知識和以及一些相似於React中的事件這類的較容易理解的知識沒作過多的敘述,至於Routing這類構建app的知識,之後有機會必定會再和你們分享。
對於剛剛接觸React的童鞋,可能看完依舊是雲裏霧裏,不過本文實在算不上教程,初學者可能仍是得看比較靠譜的教程。
以前看過一個一個比較好的React學習路徑推薦在此也分享給你們,但願對你們的React學習有幫助
學習React的基本知識;
熟悉npm
熟悉JavaScript的打包工具
瞭解ES6
學習Routing和flux(redux)
最後還要作一個小廣告,或者其實也算是對本身的一個激勵和監督,以前和 React Enlightenment這本開源書的做者聯繫,他也很是願意本身的書能讓更多人有收穫,因此就贊成我把這本書翻譯爲漢語了。這本書目前一共八章,這本書,上週我看就看完了,感受有很大收穫,對初學者也比較友好,應該好好看一道,React確定就入門了,我打算是每三四天翻譯一章,而後也發佈在此處,歡迎你們關注,但願和你們一塊兒進步。