一、什么是React
React: A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES
上面的話直譯過來就是,React是一個用於構建用戶界面的JavaScript庫。
React起源於Facebook內部項目,后來覺得不錯,在2013年6月開源。
二、學習React
我學習的時候是基於 React-Demos 這個阮一峰博客上寫的Demo庫以及他的博客學習的,基本上跟着Demo跑一次下來就差不多入門了。前提是你對JavaScript和DOM有一定的了解。
三、安裝
1.直接到官網下載
2.看着React-Demos學習的話,React-Demos中就自帶了React源碼,直接git clone或者下載zip壓縮包
四、HelloWorld
下面是使用React網頁的基本結構
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="helloworld"></div> <script type="text/jsx"> // your code React.render( <h1>Hello, World!</h1>, document.getElementById('helloworld') ); </script> </body> </html>
注意:
1.最后的script標簽的type屬性是“text/jsx”。因為React獨有的JSX語法與JacaScript不兼容,使用JSX的地方都有顯示表明type屬性為"text/jsx"。
2.React提供的兩個庫react.js和JSXTransformer.js都必須首先加載。JSXTransformer.js的作用是件JSX語法轉為JavaScript語法,這一步實際應該放到服務器完成,因為非常耗時間。
上面的代碼中用到了React.render方法,這是React最基本的方法,用於將模板轉為HTML語言並插入指定的DOM節點。
我們看到代碼中,HTML語言直接寫在JavaScript語言中,沒有任何引號,這是JSX語法,允許HTML與JavaScript混寫。
五、JSX語法
把上面的HelloWorld代碼修改一下。
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="helloJSX"></div> <script type="text/jsx"> // your code var names = ['John', 'Amin', 'Ann']; React.render( <div> { names.map(function (name) { return <h1>Hello, {name}!</h1> }) } </div>, document.getElementById('helloJSX') ); </script> </body> </html>
上面這種像XML的語法叫JSX語法。
在JSX語法中,遇到HTML標簽(以<開頭)就用HTML規則解析,遇到代碼塊(以{開頭)就用JavaScript規則解析。
上面代碼運行出來就是id為helloworld的div下再嵌套着一個div,然后再嵌套三個h1標題。
JSX還允許在模板中直接插入JavaScript變量,變量是數組就展開數組成員。
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="helloArray"></div> <script type="text/jsx"> // your code var arr = [ <h1>Hello, world!</h1>, <h2>Hello, Amin!</h2>, <h3>React is awesome</h3> ]; React.render( <div>{arr}</div>, document.getElementById('helloArray') ); </script> </body> </html>
上面代碼會在id為helloArray的div下嵌套三個套着div的標題。
我本來想看一下如果不想在標題外面再嵌套div要怎么做,暫時還沒發現。
六、組件(component)
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="helloComponent"></div> <script type="text/jsx"> // your code var HelloMessage = React.createClass({ render: function() { return <h1>hello, {this.props.name}</h1>; } }); React.render( <HelloMessage name="John"/>, document.getElementById('helloComponent') ); </script> </body> </html>
React允許代碼封裝成組件,在網頁中插入組件就像插入普通HTML標簽一樣。
React.createClass方法是用來生成一個組件類,上面的HelloMessage就是這個方法生成的一個組件類。在模板插入<HelloMessage />時和插入普通的HTML標簽一樣,但是會自動生成HelloMessage這個組件類的一個實例。在組件類中應該有自己的render方法來輸出組件。
組件可以加入任意的屬性,和HTML標簽的用法一樣。上面的HelloMessage組件中就有一個name屬性。組件屬性在組件類的this.props對象上可以獲取。
添加組件屬性的時候需要注意不要和JavaScript保留字沖突。如果要用到class和for應該這么寫:
var MyComponent = React.createClass({ render: function() { return <div className="first"><span>A Span</span></div>; } });
this.props對象屬性基本上和組件屬性對應,除了this.props.children屬性,它表示的意思是組件的所有子節點。
見下面代碼
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <script type="text/jsx"> // your code var NoteList = React.createClass({ render: function() { return ( <ol> { this.props.children.map(function (child) { return <li>{child}</li>; }) } </ol> ); } }); React.render( <NoteList> <span>Hello, world!</span> <span>Hello, Amin!</span> <span>Hello, Ann!</span> </NoteList>, document.body ); </script> </body> </html>
NoteList組件類通過this.props.children讀取NoteList組件實例下的三個span子節點,然后重新進行轉化再插入到body中。
注意子節點必須多於1個,this.props.children才會是一個數組,當它是數組時才能用map方法,否則會報錯。
我們需要知道,組件並不是真實的DOM節點,它是存在於內存中叫虛擬DOM(virtual DOM)的一種數據結構。組件插入文檔中以后才會成為真實的DOM節點。
根據React的diff算法,DOM變動都先在虛擬DOM上發生,然后將發生變動部分反映到真實DOM上。
如果需要從組件中獲取真實DOM節點,需要用到的方法叫React.findDOMNode.
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="example"></div> <script type="text/jsx"> // your code var MyComponent = React.createClass({ handleClick: function() { React.findDOMNode(this.refs.myTextInput).focus(); }, render: function() { return ( <div> <input type="text" ref="myTextInput" /> <input type="button" value="Focus the text input" onClick={this.handleClick} /> </div> ); } }); React.render( <MyComponent />, document.getElementById('example') ); </script> </body> </html>
我對上面代碼的理解是,在組件MyComponent的子節點中有一個文本輸入框,用於獲取用戶輸入,onClick綁定的事件是組件中的handleClick,讓文本框獲得焦點。
這時就需要獲得真實的DOM節點,為了做到這點,首先文本輸入框要有一個ref屬性,我對ref屬性的理解是通過這個屬性文本輸入框被分配了一個名字,通過this.refs.[refName]就能指向這個虛擬DOM的子節點,最后通過React.findDOMNode方法獲取真實DOM節點。
React把組件看成一個狀態機,一開始有一個初始狀態,組件與用戶互動導致狀態變化的話,會觸發重新渲染UI。
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="state"></div> <script type="text/jsx"> // your code var LikeButton = React.createClass({ getInitialState: function () { return {liked: false}; }, handleClick: function (event) { this.setState({liked: !this.state.liked}); }, render: function () { var text = this.state.liked ? 'like' : 'haven\'t liked'; return ( <p onClick={this.handleClick} > You {text} this. Click to toggle. </p> ); } }); React.render( <LikeButton />, document.getElementById('state') ); </script> </body> </html>
上面的效果就是你點擊div里面的文字會進行切換。
LikeButton組件中的getInitialState方法定義初始狀態,這個對象通過this.state讀取。每次點擊都會導致狀態變化,this.setState方法用於修改狀態值,每次修改之后都會自動調用this.render方法,再次渲染組件。
當組件的特性是會隨着與用戶互動而產生變化時,我們應該使用this.state,而不是this.props