參考了這篇文章:javascript
http://www.ruanyifeng.com/blog/2015/03/react.htmlhtml
其中github 安裝配置見上一篇文章(link)java
使用 React 的網頁源碼,結構大體以下。node
<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> // ** Our code goes here! ** </script> </body> </html>
上面有兩個地方須要注意:react
首先,最後一個 <script> 標籤的 type 屬性爲 text/babel 。這是由於 React 獨有的 JSX 語法,跟 JavaScript 不兼容。
凡是使用 JSX 的地方,都要加上 type="text/babel" 。 其次,上面代碼一共用了三個庫: react.js 、react-dom.js 和 Browser.js ,它們必須首先加載。
其中,react.js 是 React 的核心庫,
react-dom.js 是提供與 DOM 相關的功能,
Browser.js 的做用是將 JSX 語法轉爲 JavaScript 語法,
這一步很消耗時間,實際上線的時候,應該將它放到服務器完成。
上面命令能夠將 子目錄的 文件進行語法轉換,轉碼後的文件所有放在 子目錄。$ babel src --out-dir buildsrcjsbuild
babel命令我發如今機器上沒有安裝。須要安裝一下,用了下面兩個命令:jquery
$sudo npm install webpack -g $sudo npm install webpack babel-loader babel-core babel-preset-es2015 --save-dev -g 可是仍是沒有babel命令行,須要安裝 $ sudo npm install babel-cli -g
而後就有babel命令行了一剛。webpack
在這個目錄添加本身練習代碼的地方:git
/Users/baidu/Documents/Data/Work/Code/Self/react-demosgithub
把build目錄拷過來。web
而後在demo1裏面寫一個 index.html :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> ReactDOM.render( <h1>Hello!</h1>, document.getElementById('example') ); </script> </body> </html>
雙擊在瀏覽器打開,可以看到界面顯示。
試一試babel命令。
建立一個目錄 dist,而後 $ babel . --out-dir dist 可是發現dist目錄沒有變化。看了一下,貌似須要是 js 文件纔可以被轉換。
ReactDOM.render 是 React 的最基本方法,用於將模板轉爲 HTML 語言,並插入指定的 DOM 節點。
HTML 語言直接寫在 JavaScript 語言之中,不加任何引號,這就是 JSX 的語法
JSX 的基本語法規則:
遇到 HTML 標籤(以 <
開頭),就用 HTML 規則解析;
遇到代碼塊(以 {
開頭),就用 JavaScript 規則解析。
關於上面的內容,又寫了一段代碼(參考demo2):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var names = ['a', 'b', 'c']; ReactDOM.render( <div> { names.map(function (name) { return <div> Hello {name}!</div> }) } </div>, document.getElementById('example') ) </script> </body> </html>
注意,以上的語句都沒有加分號,其實js是不須要有分號;的。
雙擊在瀏覽器打開後,可以看到:
Hello a! Hello b! Hello c!
以上的代碼中,數組是經過map來進行映射和執行的。而若是把數組直接放在jsx模板中,數組的內容會直接展開。
以下(參考了demo03):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var arr = [ <h1>Hello,</h1>, <h2>Here!</h2> ] ReactDOM.render( <div>{arr}</div>, document.getElementById('example') ) </script> </body> </html>
React 容許將代碼封裝成組件(component),而後像插入普通 HTML 標籤同樣,在網頁中插入這個組件。
下面的代碼生成一個組件(使用React.createClass,參考demo04):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Hello = React.createClass({ render:function () { return <h1>Hello {this.props.name}</h1> } }) ReactDOM.render( <Hello name="myname"/>, document.getElementById('example') ) </script> </body> </html>
瀏覽器界面顯示:
Hello myname
模板插入 <Hello />
時,會自動生成 Hello
的一個實例(下文的"組件"都指組件類的實例)。
全部組件類都必須有本身的 render
方法,用於輸出組件。
組件類的第一個字母必須大寫,不然會報錯。
另外,組件類頂層標籤個數只能是一個,不能有多個頂層標籤,(非頂層標籤隨意),不然也會報錯。
組件的用法與原生的 HTML 標籤徹底一致,能夠任意加入屬性,組件的屬性能夠在組件類的 this.props
對象上獲取。
添加組件屬性,須要注意的是,class
屬性須要寫成 className
,for
屬性須要寫成 htmlFor
,由於 class
和 for
是 JavaScript 的保留字。
this.props
對象的屬性與組件的屬性一一對應,可是有一個例外,就是 this.props.children
屬性。它表示組件的全部子節點。
寫一段代碼以下(參考demo5):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Hello = React.createClass({ render:function () { return (<ol>{ React.Children.map(this.props.children, function (child) { return <li> {child} </li> }) }</ol>) } }) ReactDOM.render( <Hello> <span>hello</span> <span>world</span> </Hello>, document.getElementById('example') ) </script> </body> </html>
頁面代碼,加上了li:
<ol data-reactroot=""><li><span>hello</span></li><li><span>world</span></li></ol>
props.children使用注意點以下:
this.props.children 的值有三種可能: 若是當前組件沒有子節點,它就是 undefined ; 若是有一個子節點,數據類型是 object ; 若是有多個子節點,數據類型就是 array 。 因此,處理 this.props.children 的時候要當心。 React 提供一個工具方法 React.Children 來處理 this.props.children 。 咱們能夠用 React.Children.map 來遍歷子節點,而不用擔憂 this.props.children 的數據類型是 undefined 仍是 object。 更多的 React.Children 的方法,能夠參考官方文檔。 https://facebook.github.io/react/docs/top-level-api.html#react.children
組件類的PropTypes
屬性,就是用來驗證組件實例的屬性是否符合要求。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Hello = React.createClass({ propTypes: { title: React.PropTypes.string.isRequired, }, render: function () { return <h1> {this.props.title} </h1> } }) var data = 123; ReactDOM.render( <Hello title={data}/>, document.getElementById('example') ) </script> </body> </html>
運行時,會發現報錯:
Warning: Failed propType: Invalid prop `title` of type `number` supplied to `Hello`, expected `string`.
另外,能夠用getDefaultProps設置props的默認值:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Hello = React.createClass({ getDefaultProps: function () { return { title: "Hello" } }, propTypes: { title: React.PropTypes.string.isRequired, }, render: function () { return <h1> {this.props.title} </h1> } }) var data = 123; ReactDOM.render( <Hello/>, document.getElementById('example') ) </script> </body> </html>
瀏覽器顯示:
Hello
組件並非真實的 DOM 節點,而是存在於內存之中的一種數據結構,叫作虛擬 DOM (virtual DOM)。
只有當它插入文檔之後,纔會變成真實的 DOM 。根據 React 的設計,全部的 DOM 變更,都先在虛擬 DOM 上發生,
而後再將實際發生變更的部分,反映在真實 DOM上,這種算法叫作 DOM diff ,它能夠極大提升網頁的性能表現。
DOM diff的算法能夠詳見:link
有時須要從組件獲取真實 DOM 的節點,這時就要用到 ref
屬性.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Hello = React.createClass({ handleClick: function () { this.refs.myTextInput.focus(); }, render: function () { return ( <div> <input type="text" ref="myTextInput"/> <input type="button" value="Focus" onClick={this.handleClick}/> </div> ) } }) var data = 123; ReactDOM.render( <Hello/>, document.getElementById('example') ) </script> </body> </html>
爲了作到這一點,文本輸入框必須有一個 ref
屬性,而後 this.refs.[refName]
就會返回這個真實的 DOM 節點。
須要注意的是,因爲 this.refs.[refName] 屬性獲取的是真實 DOM ,因此必須等到虛擬 DOM 插入文檔之後,才能使用這個屬性,不然會報錯。
上面代碼中,經過爲組件指定 Click 事件的回調函數,確保了只有等到真實 DOM 發生 Click 事件以後,纔會讀取 this.refs.[refName] 屬性。
React 組件支持不少事件,除了 Click 事件之外,還有 KeyDown 、Copy、Scroll 等,完整的事件清單請查看官方文檔。
https://facebook.github.io/react/docs/events.html#supported-events
因爲 this.props
和 this.state
都用於描述組件的特性,可能會產生混淆。一個簡單的區分方法是,this.props
表示那些一旦定義,就再也不改變的特性,而 this.state
是會隨着用戶互動而產生變化的特性。
看以下代碼,demo8:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Hello = 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> ) } }) var data = 123; ReactDOM.render( <Hello/>, document.getElementById('example') ) </script> </body> </html>
React 的一大創新,就是將組件當作是一個狀態機,一開始有一個初始狀態,而後用戶互動,致使狀態變化,從而觸發從新渲染 UI。
它的
方法就修改狀態值,每次修改之後,自動調用 getInitialState
方法用於定義初始狀態,也就是一個對象,這個對象能夠經過 this.state
屬性讀取。當用戶點擊組件,致使狀態變化,this.setStatethis.render
方法,再次渲染組件。
文本輸入框的值,不能用 this.props.value
讀取,而要定義一個 onChange
事件的回調函數,經過 event.target.value
讀取用戶輸入的值。
textarea
元素、select
元素、radio
元素都屬於這種狀況,更多介紹請參考官方文檔。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Hello = React.createClass({ getInitialState: function () { return {value: 'Hello!'} }, handleChange: function (event) { this.setState({value: event.target.value}) }, render: function () { var text = this.state.value; return ( <div> <input type="text" value={text} onChange={this.handleChange}/> <p>{text}</p> </div> ) } }) var data = 123; ReactDOM.render( <Hello/>, document.getElementById('example') ) </script> </body> </html>
以上參考demo09。瀏覽器頁面以下:
hidanfeingd,aennga
hidanfeingd,aennga
上面文本框和下面的文本內容是同步的。
組件的生命週期分紅三個狀態:
Mounting:已插入真實 DOM
Updating:正在被從新渲染
Unmounting:已移出真實 DOM
React 爲每一個狀態都提供了兩種處理函數,will
函數在進入狀態以前調用,did
函數在進入狀態以後調用,三種狀態共計五種處理函數。
componentWillMount() componentDidMount() componentWillUpdate(object nextProps, object nextState) componentDidUpdate(object prevProps, object prevState) componentWillUnmount()
此外,React 還提供兩種特殊狀態的處理函數。
componentWillReceiveProps(object nextProps):已加載組件收到新的參數時調用 shouldComponentUpdate(object nextProps, object nextState):組件判斷是否從新渲染時調用
須要找時間詳細閱讀一下官方文檔:link
These methods are called when an instance of a component is being created and inserted into the DOM:
An update can be caused by changes to props or state. These methods are called when a component is being re-rendered:
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()
This method is called when a component is being removed from the DOM:
Each component also provides some other APIs:
參考Demo10,寫了一個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Hello = React.createClass({ getInitialState: function () { return {opacity: 1.0} }, componentDidMount: function () { this.timer = setInterval(function () { var opacity = this.state.opacity; opacity -= .05; if (opacity < 0.1) { opacity = 1.0; } this.setState({ opacity: opacity }) }.bind(this), 100) }, render: function () { return ( <div style={{opacity: this.state.opacity}}> Hello {this.props.name} </div> ) } }) var data = 123; ReactDOM.render( <Hello name="Bill"/>, document.getElementById('example') ) </script> </body> </html>
界面上面會有一行字,深淺逐漸變化。
經過 componentDidMount
方法設置一個定時器,每隔100毫秒,就從新設置組件的透明度,從而引起從新渲染。
另外,組件的style
屬性的設置方式也值得注意:
不能寫成 style="opacity:{this.state.opacity};"
而要寫成 style={{opacity: this.state.opacity}} 這是由於 React 組件樣式是一個對象,因此第一重大括號表示這是 JavaScript 語法,第二重大括號表示樣式對象。
參考: https://facebook.github.io/react/tips/inline-styles.html
組件的數據來源,一般是經過 Ajax 請求從服務器獲取,能夠使用 componentDidMount
方法設置 Ajax 請求,
等到請求成功,再用 this.setState
方法從新渲染 UI。
開始沒有任何反應,應該是跨域了。結果不是,是缺了jquery的庫。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> <script src="../build/jquery.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Hello = React.createClass({ getInitialState: function () { return { username: '', lastGistUrl: '' } }, componentDidMount: function () { //alert("hihi") $.get(this.props.source, function (result) { //alert(result); var lastGist = result[0]; if (this.isMounted()) { this.setState({ username: lastGist.owner.login, lastGistUrl: lastGist.html_url }) } }.bind(this)) }, render: function () { return ( <div> Hello {this.state.username} <a href={this.state.lastGistUrl}>here</a> </div> ) } }) ReactDOM.render( <Hello source="https://api.github.com/users/octocat/gists"/>, document.getElementById('example') ) </script> </body> </html>
奇怪的是,我訪問其餘url,老是存在跨域問題,訪問api.github就不會出現這樣的問題。。
而且能夠直接在html標籤裏面用 getJSON,而後根據狀態來處理,以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../build/react.js"></script> <script src="../build/react-dom.js"></script> <script src="../build/browser.min.js"></script> <script src="../build/jquery.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> var Hello = React.createClass({ getInitialState: function () { return { loading: true, error: null, data: null } }, componentDidMount: function () { this.props.promise.then( value => this.setState({loading: false, data: value}), error => this.setState({loading: false, error: error}) ) }, render: function () { if (this.state.loading) { return <span> Loading... </span> } else if (this.state.error !== null) { return <span>Error: {this.state.error}</span> } else { var repos = this.state.data.items; var repoList = repos.map( function (repo, index) { return ( <li key={index}> <a href={repo.html_url}>{repo.name}</a> ({repo.stargazers_count} stars) <br/> {repo.description} </li> ) } ) return ( <main> <h1>Most Popular Javascript Project in Github</h1> <ol>{repoList}</ol> </main> ) } } }) ReactDOM.render( <Hello promise={$.getJSON('https://api.github.com/search/repositories?q=javascript&sort=stars')}/>, document.getElementById('example') ) </script> </body> </html>
瀏覽器裏面能夠看到,先是loading,而後有下面的結果:
最後,還有一個demo 13,是介紹react server,首屏渲染的。
安裝方法,先到demo13目錄裏:
# install the dependencies in demo13 directory $ npm install # translate all jsx file in src subdirectory to js file $ npm run build # launch http server $ node server.js
而後 localhost:3000 能訪問,可是貌似有一些界面的問題。有機會再看。