React最初來自Facebook內(nèi)部的廣告系統(tǒng)項(xiàng)目既峡,項(xiàng)目實(shí)施過程中前端開發(fā)遇到了巨大挑戰(zhàn)羡榴,代碼變得越來越臃腫且混亂不堪,難以維護(hù)运敢。他們又對市場的現(xiàn)有的前端mvc框架都不滿意校仑。于是他們決定拋開很多所謂的“最佳實(shí)踐”忠售,重新思考前端界面的構(gòu)建方式,就決定自己寫一套框架來解決這些問題迄沫,然后就有了React稻扬。
后來發(fā)現(xiàn)React框架在開發(fā)中很好用,于是FB投入了更多的人力去開發(fā)這套框架羊瘩,最后在2013年5月宣布開源泰佳。
由于 React 的設(shè)計(jì)思想極其獨(dú)特,屬于革命性創(chuàng)新尘吗,性能出眾乐纸,代碼邏輯卻非常簡單。所以摇予,越來越多的人開始關(guān)注和使用汽绢,認(rèn)為它可能引領(lǐng)未來用戶界面開發(fā)的主流框架。
React 這么火侧戴,那么它到底有什么牛逼的地方宁昭?
特點(diǎn)及優(yōu)勢
1.虛擬dom (開發(fā)時(shí)候不需要在頁面中寫任何dom元素)
2.jsx語法(寫頁面時(shí)候使用javascript xml格式的語法)
3.組件化開發(fā)(react最核心的思想是將頁面中任何一個(gè)區(qū)域或者元素都看成是一個(gè)組件 Component)
4.單向數(shù)據(jù)流(組件和后端之間的數(shù)據(jù)是單向的,從后端流動(dòng)到react組件中)
5.組件生命周期(任何一個(gè)組件在dom中都具有一個(gè)完整的聲明周期酗宋,組件初始化的時(shí)候開始积仗,組件被移除的時(shí)候消失,從而保證性能的優(yōu)越)
虛擬DOM
虛擬DOM則是在DOM的基礎(chǔ)上建立了一個(gè)抽象層蜕猫,我們對數(shù)據(jù)和狀態(tài)所做的任何改動(dòng)寂曹,都會(huì)被自動(dòng)且高效的同步到虛擬DOM,最后再批量同步到DOM中渺氧。
虛擬DOM會(huì)使得App只關(guān)心數(shù)據(jù)和組件的執(zhí)行結(jié)果,中間產(chǎn)生的DOM操作不需要App干預(yù)蹬屹,而且通過虛擬DOM來生成DOM侣背,會(huì)有一項(xiàng)非晨可觀收益——-性能贩耐。
React會(huì)在內(nèi)存中維護(hù)一個(gè)虛擬DOM樹,當(dāng)我們對這個(gè)樹進(jìn)行讀或?qū)懙臅r(shí)候潮太,實(shí)際上是對虛擬DOM進(jìn)行的朋沮。當(dāng)數(shù)據(jù)變化時(shí)蒂胞,然后React會(huì)自動(dòng)更新虛擬DOM骗随,然后拿新的虛擬DOM和舊的虛擬DOM進(jìn)行對比,找到有變更的部分,得出一個(gè)Patch松逊,然后將這個(gè)Patch放到一個(gè)隊(duì)列里,最終批量更新這些Patch到DOM中暇矫。
缺陷——首次渲染大量DOM時(shí)因?yàn)槎嗔艘粚犹摂MDOM的計(jì)算,會(huì)比innerHTML插入方式慢择吊,所以使用時(shí)盡量不要一次性渲染大量DOM李根。
JSX語法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 核心 -->
<script src="build/react.min.js"></script>
<!-- 渲染dom -->
<script src="build/react-dom.min.js"></script>
<!-- 把jsx、es6轉(zhuǎn)換成js几睛、es5 -->
<script src="build/browser.min.js"></script>
</head>
<body>
<div id="root"></div>
<!-- bebel是工具房轿,把es6轉(zhuǎn)換成es5 -->
<script type="text/babel">
ReactDOM.render(<h1>React</h1>,document.getElementById("root"))
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 核心 -->
<script src="build/react.min.js"></script>
<!-- 渲染dom -->
<script src="build/react-dom.min.js"></script>
<!-- 把jsx、es6轉(zhuǎn)換成js所森、es5 -->
<script src="build/browser.min.js"></script>
</head>
<body>
<div id="root"></div>
<!-- bebel是工具冀续,把es6轉(zhuǎn)換成es5 -->
<script type="text/babel">
var arr = ["張三","李四","王五"];
ReactDOM.render(<div>{
arr.map(function(ele,index){
return <p>{ele}</p>
})
}</div>,document.getElementById("root"))
</script>
</body>
</html>
JSX 的基本語法規(guī)則:遇到 HTML 標(biāo)簽(以 < 開頭),就用 HTML 規(guī)則解析必峰;遇到代碼塊(以 { 開頭)洪唐,就用 JavaScript 規(guī)則解析。
組件
模塊化中的模塊一般指的是為了實(shí)現(xiàn)某些功能的代碼片段吼蚁,模塊化主要實(shí)現(xiàn)了代碼分工凭需,分模塊完成,強(qiáng)調(diào)的是功能肝匆;
組件是組成頁面的部件粒蜈,內(nèi)部封裝了組件的結(jié)構(gòu)、樣式旗国、行為枯怖,更多的考慮代碼的復(fù)用性,頁面結(jié)構(gòu)的實(shí)現(xiàn)
在組件化開發(fā)中能曾,一個(gè)模塊可能需要多個(gè)組件(比如評價(jià)的模塊度硝,可能需要列表的組件、按鈕的組件寿冕、用戶信息組件…)蕊程,但是有些模塊也可以跟組件沒有關(guān)系,只是實(shí)現(xiàn)一些功能驼唱。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 核心 -->
<script src="build/react.min.js"></script>
<!-- 渲染dom -->
<script src="build/react-dom.min.js"></script>
<!-- 把jsx藻茂、es6轉(zhuǎn)換成js、es5 -->
<script src="build/browser.min.js"></script>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
.header{
position: fixed;left: 0;top: 0;height: 44px;border-bottom: 1px solid #ccc; text-align: center;width: 100%;line-height: 44px;
}
.footer{
position: fixed;left: 0;bottom: 0;height: 44px;border-top: 1px solid #ccc; width: 100%;line-height: 44px;
list-style: none;
}
.footer li{
float: left;width: 25%;text-align: center;
}
.content{
padding-top: 45px;padding-bottom: 45px;
}
</style>
</head>
<body>
<div id="root"></div>
<!-- bebel是工具,把es6轉(zhuǎn)換成es5 -->
<!-- 組件都是用面向?qū)ο髽?gòu)建的辨赐,組件經(jīng)常會(huì)被復(fù)用优俘,用面向?qū)ο髮懣梢宰龅焦灿茫M件名稱要大寫 -->
<!-- 不能用class掀序,class在js中是保留字符 -->
<script type="text/babel">
var Header = React.createClass({
render:function(){
return <div className="header">網(wǎng)站頭部</div>
}
})
var Footer = React.createClass({
render:function(){
return <ul className="footer">
<li>首頁</li>
<li>列表</li>
<li>購物車</li>
<li>我的</li>
</ul>
}
})
var Content = React.createClass({
render:function(){
return <div className="content">內(nèi)容</div>
}
})
var IndexPage = React.createClass({
render:function(){
return <div className="page">
<Header/>
<Footer/>
<Content/>
</div>
}
})
ReactDOM.render(<IndexPage/>,document.getElementById("root"))
</script>
<!-- 組件里面render的內(nèi)容帆焕,就是當(dāng)組件被調(diào)用顯示的內(nèi)容 -->
</body>
</html>
單項(xiàng)數(shù)據(jù)流
props(properties 特性)是在調(diào)用時(shí)候被調(diào)用者設(shè)置的,只設(shè)置一次森枪,一般沒有額外變化视搏,可以把任意類型的數(shù)據(jù)傳遞給組件审孽,盡可能的吧props當(dāng)做數(shù)據(jù)源县袱,不要在組件內(nèi)部設(shè)置props。
組件傳參:
1.this.props.children
2.this.props.xxx
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 核心 -->
<script src="build/react.min.js"></script>
<!-- 渲染dom -->
<script src="build/react-dom.min.js"></script>
<!-- 把jsx佑力、es6轉(zhuǎn)換成js式散、es5 -->
<script src="build/browser.min.js"></script>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
.header{
position: fixed;left: 0;top: 0;height: 44px;border-bottom: 1px solid #ccc; text-align: center;width: 100%;line-height: 44px;
}
.footer{
position: fixed;left: 0;bottom: 0;height: 44px;border-top: 1px solid #ccc; width: 100%;line-height: 44px;
list-style: none;
}
.footer li{
float: left;width: 25%;text-align: center;
}
.content{
padding-top: 45px;padding-bottom: 45px;
}
</style>
</head>
<body>
<div id="root"></div>
<!-- bebel是工具,把es6轉(zhuǎn)換成es5 -->
<!-- 組件都是用面向?qū)ο髽?gòu)建的打颤,組件經(jīng)常會(huì)被復(fù)用暴拄,用面向?qū)ο髮懣梢宰龅焦灿茫M件名稱要大寫 -->
<!-- 不能用class编饺,class在js中是保留字符 -->
<script type="text/babel">
var Header = React.createClass({
render:function(){
return <div className="header">{this.props.title}</div>
}
})
var Footer = React.createClass({
render:function(){
return <ul className="footer">
<li>首頁</li>
<li>列表</li>
<li>購物車</li>
<li>我的</li>
</ul>
}
})
var Content = React.createClass({
render:function(){
return <div className="content">{this.props.children}</div>
}
})
var List = React.createClass({
render:function(){
return <ul>
<li>商品1</li>
<li>商品2</li>
<li>商品3</li>
</ul>
}
})
var IndexPage = React.createClass({
render:function(){
return <div className="page">
<Header title="首頁" />
<Footer/>
<Content>
<List/>
</Content>
</div>
}
})
ReactDOM.render(<IndexPage/>,document.getElementById("root"))
</script>
<!-- 組件里面render的內(nèi)容乖篷,就是當(dāng)組件被調(diào)用顯示的內(nèi)容 -->
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 核心 -->
<script src="build/react.min.js"></script>
<!-- 渲染dom -->
<script src="build/react-dom.min.js"></script>
<!-- 把jsx、es6轉(zhuǎn)換成js透且、es5 -->
<script src="build/browser.min.js"></script>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
.header{
position: fixed;left: 0;top: 0;height: 44px;border-bottom: 1px solid #ccc; text-align: center;width: 100%;line-height: 44px;
}
.footer{
position: fixed;left: 0;bottom: 0;height: 44px;border-top: 1px solid #ccc; width: 100%;line-height: 44px;
list-style: none;
}
.footer li{
float: left;width: 25%;text-align: center;
}
.content{
padding-top: 45px;padding-bottom: 45px;
}
</style>
</head>
<body>
<div id="root"></div>
<!-- bebel是工具撕蔼,把es6轉(zhuǎn)換成es5 -->
<!-- 組件都是用面向?qū)ο髽?gòu)建的,組件經(jīng)常會(huì)被復(fù)用秽誊,用面向?qū)ο髮懣梢宰龅焦灿镁ň冢M件名稱要大寫 -->
<!-- 不能用class,class在js中是保留字符 -->
<script type="text/babel">
var Header = React.createClass({
render:function(){
return <div className="header">{this.props.title}</div>
}
})
var Footer = React.createClass({
render:function(){
return <ul className="footer">
<li>首頁</li>
<li>列表</li>
<li>購物車</li>
<li>我的</li>
</ul>
}
})
var Content = React.createClass({
render:function(){
return <div className="content">{this.props.children}</div>
}
})
var List = React.createClass({
render:function(){
return <ul>
{this.props.listData.map((ele,index)=>{
return <li>商品{ele}</li>
})}
</ul>
}
})
var IndexPage = React.createClass({
render:function(){
return <div className="page">
<Header title="首頁" />
<Footer/>
<Content>
<List listData={["1","2","3"]}/>
</Content>
</div>
}
})
ReactDOM.render(<IndexPage/>,document.getElementById("root"))
</script>
<!-- 組件里面render的內(nèi)容锅论,就是當(dāng)組件被調(diào)用顯示的內(nèi)容 -->
</body>
</html>
組件的生命周期
隨著該組件的props(數(shù)據(jù))或者state(狀態(tài))發(fā)生改變讼溺,它的DOM表現(xiàn)也將有相應(yīng)的變化,一個(gè)組件就是一個(gè)狀態(tài)機(jī):對于特定的輸入最易,它總會(huì)返回一致的輸出怒坯。 React為每個(gè)組件提供了生命周期鉤子函數(shù)去響應(yīng)不同的時(shí)刻,組件的生命周期分為三個(gè)部分:(1)實(shí)例化藻懒;(2)存在期敬肚;(3)銷毀&清理期。
鉤子函數(shù)類似有回調(diào)函數(shù)(callback)束析,但他是在方法內(nèi)容一開始調(diào)用的艳馒,而callback是在事件結(jié)束調(diào)用的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 核心 -->
<script src="build/react.min.js"></script>
<!-- 渲染dom -->
<script src="build/react-dom.min.js"></script>
<!-- 把jsx、es6轉(zhuǎn)換成js弄慰、es5 -->
<script src="build/browser.min.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
//getInitialState給組件設(shè)置默認(rèn)的狀態(tài)
//setState是React方法來修改state第美,視圖可以自動(dòng)更新
var Switch = React.createClass({
getInitialState:function(){
return{
show:false
}
console.log("設(shè)置初始狀態(tài)")
},
changeShow:function(){
this.setState({
show:!this.state.show
})
},
componentWillMount:function(){
console.log("即將添加")
},
render:function(){
return (
<div id="warp" ref="warp">
<button onClick={this.changeShow} ref="btn">切換</button>
<div style={{display:this.state.show?'block':'none'}}>虛擬DOM,組件化開發(fā)陆爽,生命周期什往,jsx語法,單項(xiàng)數(shù)據(jù)流</div>
</div>
)
},
componentDidMount:function(){
//獲取真實(shí)dom慌闭,請求數(shù)據(jù)
//this.refs或取到的是設(shè)置了ref屬性的dom集合
console.log("添加完成")
console.log(this.refs)
}
})
ReactDOM.render(<Switch/>,document.getElementById("root"))
</script>
</html>
實(shí)例化階段(在組件調(diào)用之前的——?jiǎng)?chuàng)建階段)
getDefaultProps方法發(fā)生在創(chuàng)建組件類的時(shí)候别威,即調(diào)用React.createClass的時(shí)候。這個(gè)階段只會(huì)觸發(fā)一個(gè)getDefaultProps方法驴剔,給this.props作為該組件的默認(rèn)屬性省古。
props屬性是一個(gè)對象,是組件用來接收外面?zhèn)鱽淼膮?shù)的組件內(nèi)部是不允許修改自己的props屬性的丧失,只能通過父組件來修改豺妓。在getDefaultProps方法中,是可以設(shè)定props默認(rèn)值的布讹。
getInitialState 初始化組件的state的值琳拭,其返回值會(huì)賦值給組件的this.state屬性。對于組件的每個(gè)實(shí)例來說描验,這個(gè)方法的調(diào)用次數(shù)有且只有一次白嘁。
componentWillMount 此方法會(huì)在完成首次渲染之前被調(diào)用。這也是在render方法調(diào)用前可以修改組件state的最后一次機(jī)會(huì)膘流。
render 生成頁面需要的虛擬DOM結(jié)構(gòu)絮缅,用來表示組件的輸出。render方法需要滿足:(1)只能通過this.props和this.state訪問數(shù)據(jù)睡扬;(2)可以返回null盟蚣、false或者任何React組件;(3)只能出現(xiàn)一個(gè)頂級組件卖怜;(4)必需純凈屎开,意味著不能改變組件的狀態(tài)或者修改DOM的輸出。
componentDidMount 該方法發(fā)生在render方法成功調(diào)用并且真實(shí)的DOM已經(jīng)被渲染之后马靠,在該函數(shù)內(nèi)部可以通過this.getDOMNode()來獲取當(dāng)前組件的節(jié)點(diǎn)奄抽。然后就可以像Web開發(fā)中的那樣操作里面的DOM元素了。
存在期
用戶改如果變了組件的state甩鳄,或者要展示的數(shù)據(jù)發(fā)生改變逞度,這時(shí)候需要重新渲染組件。
componentWillReceiveProps 在任意時(shí)刻妙啃,組件的props都可以通過父輩組件來更改档泽。當(dāng)組件接收到新的props(這里不同于state)時(shí)俊戳,會(huì)觸發(fā)該函數(shù),我們同時(shí)也獲得更改props對象及更新state的機(jī)會(huì)馆匿。
shouldComponentUpdate 該方法用來攔截新的props和state抑胎,然后開發(fā)者可以根據(jù)自己設(shè)定邏輯,做出要不要更新render的決定渐北,讓它更快(當(dāng)props 發(fā)生改變阿逃,或者調(diào)用了setState方法,調(diào)用了setState 方法哪怕state沒有改變也會(huì)觸發(fā))
componentWillUpdate 與componentWillMount方法類似赃蛛,組件上會(huì)接收到新的props或者state渲染之前恃锉,調(diào)用該方法。但是不可以在該方法中更新state和props呕臂。
render 生成頁面需要的虛擬DOM結(jié)構(gòu)破托,并返回該結(jié)構(gòu)。
componentDidUpdate 與componentDidMount類似诵闭,更新已經(jīng)渲染好的DOM炼团。
demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 核心 -->
<script src="build/react.min.js"></script>
<!-- 渲染dom -->
<script src="build/react-dom.min.js"></script>
<!-- 把jsx澎嚣、es6轉(zhuǎn)換成js疏尿、es5 -->
<script src="build/browser.min.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
var ProductList = React.createClass({
getDefaultProps:function () {
//設(shè)置默認(rèn)數(shù)據(jù)
return {
listData:[]
}
},
fnClick:function (index) {
alert(index)
},
render:function () {
return (
<ul>
{
this.props.listData.map(function (ele,index) {
return <li key={index} onClick={()=>this.fnClick(index)}>{ele}</li>
}.bind(this))
}
</ul>
)
}
});
var IndexPage = React.createClass({
render:function () {
return (
<div>
<header>頭部</header>
<ProductList listData={[1,2,3,4]}/>
<footer>底部</footer>
</div>
)
}
});
ReactDOM.render(<IndexPage/>,document.getElementById("root"));
</script>
</html>