標簽:前端開發 ReactJS
官網
官網中文鏡像(推薦)
阮一峰老師的教程
菜鳥網的教程
一看就懂的ReactJS入門教程(語法有點過期了)
關鍵庫:
react.js; react-dom.js; browser.min.js;
前言: ReactJS產生的原因背景
在Web開發中,我們總需要將變化的數據實時反應到UI上,這時就需要對DOM進行操作。而復雜或頻繁的DOM操作通常是性能瓶頸產生的原因(如何進行高性能的復雜DOM操作通常是衡量一個前端開發人員技能的重要指標)。React為此引入了虛擬DOM(Virtual DOM)的機制:在瀏覽器端用Javascript實現了一套DOM API。基于React進行開發時所有的DOM構造都是通過虛擬DOM進行,每當數據變化時,React都會重新構建整個DOM樹,然后React將當前整個DOM樹和上一次的DOM樹進行對比,得到DOM結構的區別,然后僅僅將需要變化的部分進行實際的瀏覽器DOM更新。而且React能夠批處理虛擬DOM的刷新,在一個事件循環(Event Loop)內的兩次數據變化會被合并,例如你連續的先將節點內容從A變成B,然后又從B變成A,React會認為UI不發生任何變化,而如果通過手動控制,這種邏輯通常是極其復雜的。盡管每一次都需要構造完整的虛擬DOM樹,但是因為虛擬DOM是內存數據,性能是極高的,而對實際DOM進行操作的僅僅是Diff部分,因而能達到提高性能的目的。這樣,在保證性能的同時,開發者將不再需要關注某個數據的變化如何更新到一個或多個具體的DOM元素,而只需要關心在任意一個數據狀態下,整個界面是如何Render的。
如果你像在90年代那樣寫過服務器端Render的純Web頁面那么應該知道,服務器端所要做的就是根據數據Render出HTML送到瀏覽器端。如果這時因為用戶的一個點擊需要改變某個狀態文字,那么也是通過刷新整個頁面來完成的。服務器端并不需要知道是哪一小段HTML發生了變化,而只需要根據數據刷新整個頁面。換句話說,任何UI的變化都是通過整體刷新來完成的。而React將這種開發模式以高性能的方式帶到了前端,每做一點界面的更新,你都可以認為刷新了整個頁面。至于如何進行局部更新以保證性能,則是React框架要完成的事情。
1.代碼生成標簽,插入到指定位置
jsx/babel語言 JS,HTML混寫
<body>
<div id="example"></div>
<script type="text/babel">
ReactDOM.render(
<h2>Hello,world</h2>,
document.getElementById('example')
);
</script>
</body>
div#example 作為容器存放生成的元素,
生成元素:ReactDOM.render(<tag>{codehere}</tag>, container);
注意:語句結尾可以不加分號,不會出錯,有些語句加了分號之后就會出錯,因此加分號要小心。
2.自定義標簽(組件)
<script type="text/babel">
var MyTag = React.createClass({
render:function(){ return<h1>{this.props.name}</h1> }
}); //this.props.xxx 調用自定義屬性;
ReactDOM.render(<MyTag name="zhe" />, document.getElementById('example') );
</script>
自定義標簽要一定要用在render中,直接用在html無效。
后面demo為了方便就不寫ReactDOM.render()了,實際練習時要加上
注意類名一定要首字母大寫,createClass參數是一個對象,
添加組件屬性,有一個地方需要注意,對于要調用class屬性與for屬性,屬性名不能直接寫 class 或者for,這是因為 class 和 for 是 JavaScript 的保留字。用className="" htmlFor=""
代替,這樣css一樣可以.class
找到該元素
3.可嵌套子元素的自定義標簽
<script type="text/babel">
var MyList = React.createClass({
render:function(){
return(
<ul>{ React.Children.map(this.props.children,
function(child){return<li>{child}</li>} ) }</ul>
)} //注意大小寫,props.children 用于獲取子元素
});
// 使用
// <MyList>
// <span>item1</span>
// <span>item2</span>
//</MyList>
</script>
一種映射的方法:array.map(function(item){console.log(item)})
遍歷array每次輸出一個item。
另外如果數組是一系列標簽,可以寫在容器標簽內自動展開,如<div>{array}</div>
。
React.Children.map
該方法遍歷children,而不用理會children因size不同(0,1,1+)而返回的不同類型(undefine,object, array)
4. 組件的參數檢查與默認值
<script type="text/babel">
var MyTag = React.createClass({
getDefaultProps: function(){ return {title:"hello"} }, //默認值
propTypes: {title: React.PropTypes.string.isRequired }, //表示title是必須,并且是字符串
render: function(){ return<h1>{this.props.title}</h1>}
});
//<MyTag title={123} /> 控制臺報錯,但一樣會顯示在頁面
</script>
5. 獲取組件內部標簽的DOM節點
內部標簽定義一個ref屬性,組件使用this.refs.xxx內獲取節點
var MyInput = React.createClass({
clickEvent:function(){ this.refs.tagInside.focus() }, //clickEvent的名字可隨意
render:function(){ return(
<div>
<input type="text" ref="tagInside"/>
<input type="button" value="focus" onClick={this.clickEvent} />
</div> )
}
});
6. 組件的狀態this.state
標記組件的狀態,用于改變顯示的文字,樣式等。
var MyInput = React.createClass({
getInitialState: function(){ return { liked: false } },//定義應有的狀態與初始化,用鍵值對形式
clickEvent:function() { this.setState({liked: !this.state.liked }) },//this.setState({}) 改變狀態值
render:function() {
var text =this.state.liked ? "liked" : "unliked";
return<input type="button" value={text} onClick={this.clickEvent} />
}
});
每次setState后都會自動調用render,因此可以改變樣式。
注意this.state與this.props的區別,state可以在交互中改變,可讀可寫,props定義了了就不能改變,只讀。
7. 獲取表單的輸入
輸入表單屬于交互,改變this.state
var MyInput = React.createClass({
getInitialState: function() { return {value: "default"} },
changeEvent:function(e) { this.setState({value: e.target.value}) },
render:functiion(){ return(
<div>
<input type="text" value={text} onChange={this.changeEvent} />
<p> {text}</p>
</div> )
} //注意大小寫
});
因為每次改變狀態都會調用render,因此如果input里的value設為常數,那么輸入文本就不會顯示改變
8. 組件的生命周期
分為 mounting: 已插入到真實的DOM中;
updating:被渲染中;
unmounting 已經移出真實的DOM
有兩種回調函數:will 在進入某一步前調用,did進入某一步后調用,共5個
componentWillMount();
componentDidMount();
componentWillUpdate(oNextProps, oNextState);
componentDidUpdate(oPrevProps, oPrevState);
componentWillUnmount();
這個比較復雜,還是看官網比較好:https://facebook.github.io/react/docs/working-with-the-browser.html#component-lifecycle
9. AJAX
componentDidMount 中可以用jQuery.get() 實現,React 本身沒有任何依賴,完全可以不用jQuery,而使用其他庫。
<script type="text/babel">
var UserGist = React.createClass({
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
};
},
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
if (this.isMounted()) {
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}
}.bind(this));
},
render: function() {
return (
<div>
{this.state.username}'s last gist is <a href={this.state.lastGistUrl}>here</a>.
</div>
);
}
});
ReactDOM.render(
<UserGist source="https://api.github.com/users/octocat/gists" />,
document.body
);
</script>
10. 傳入promise對象
<script type="text/babel">
var RepoList = React.createClass({
getInitialState: function() {return {loading:true, data:null, error: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) {
return <span>Error: {this.state.error.message} </span>
} else {
var repos = this.state.data.items;
var list = repos.map(function(repo){
return (
<li>
<a href={repo.html_url}> {repo.name} </a>
({repo.stargazers_count} stars) <br/>
{repo.description}
</li> )
})
}
return (<div>
<h1>Most Popular JavaScript Projects in Github</h1>
<ol>{list}</ol>
</div>)
}
})
ReactDOM.render(
<RepoList promise={$.getJSON('https://api.github.com/search/repositories?q=javascript&sort=stars')} />,
document.getElementById("example")
)
</script>
總結
上面是react的簡單用法,更多更詳細的解釋請看官方文檔。
react還可以配合flux框架使用,接下來可以學習這方面的相關知識。