React的起源和發(fā)展
起初facebook在建設(shè)instagram(圖片分享)的時(shí)候嘞,因?yàn)闋砍兜揭粋€(gè)東東叫數(shù)據(jù)流,那為了處理數(shù)據(jù)流并且還要考慮好性能方面的問題嘞卷雕,F(xiàn)acebook開始對市場上的各種前端MVC框架去進(jìn)行一個(gè)研究幸逆,然而并沒有看上眼的却嗡,于是Facebook覺得纵隔,還是自己開發(fā)一個(gè)才是最棒的翻诉,那么他們決定拋開很多所謂的“最佳實(shí)踐”,重新思考前端界面的構(gòu)建方式巨朦,他們就自己開發(fā)了一套,果然大牛創(chuàng)造力還是很強(qiáng)大的剑令。
React的出發(fā)點(diǎn)
基于HTML的前端界面開發(fā)正變得越來越復(fù)雜糊啡,其本質(zhì)問題基本都可以歸結(jié)于如何將來自于服務(wù)器端或者用戶輸入的動(dòng)態(tài)數(shù)據(jù)高效的反映到復(fù)雜的用戶界面上。而來自Facebook的React框架正是完全面向此問題的一個(gè)解決方案吁津,按官網(wǎng)描述棚蓄,其出發(fā)點(diǎn)為:用于開發(fā)數(shù)據(jù)不斷變化的大型應(yīng)用程序(Building large applications with data that changes over time)。相比傳統(tǒng)型的前端開發(fā)碍脏,React開辟了一個(gè)相當(dāng)另類的途徑梭依,實(shí)現(xiàn)了前端界面的高性能高效率開發(fā)。
React與傳統(tǒng)MVC的關(guān)系
輕量級的視圖層框架典尾! React不是一個(gè)完整的MVC框架役拴,最多可以認(rèn)為是MVC中的V(View),甚至React并不非常認(rèn)可MVC開發(fā)模式钾埂;
React高性能的體現(xiàn):虛擬DOM
React高性能的原理:
在Web開發(fā)中我們總需要將變化的數(shù)據(jù)實(shí)時(shí)反應(yīng)到UI上河闰,這時(shí)就需要對DOM進(jìn)行操作。而復(fù)雜或頻繁的DOM操作通常是性能瓶頸產(chǎn)生的原因(如何進(jìn)行高性能的復(fù)雜DOM操作通常是衡量一個(gè)前端開發(fā)人員技能的重要指標(biāo))褥紫。
React為此引入了虛擬DOM(Virtual DOM)的機(jī)制:在瀏覽器端用Javascript實(shí)現(xiàn)了一套DOM API姜性。基于React進(jìn)行開發(fā)時(shí)所有的DOM構(gòu)造都是通過虛擬DOM進(jìn)行髓考,每當(dāng)數(shù)據(jù)變化時(shí)部念,React都會(huì)重新構(gòu)建整個(gè)DOM樹,然后React將當(dāng)前整個(gè)DOM樹和上一次的DOM樹進(jìn)行對比氨菇,得到DOM結(jié)構(gòu)的區(qū)別儡炼,然后僅僅將需要變化的部分進(jìn)行實(shí)際的瀏覽器DOM更新。而且React能夠批處理虛擬DOM的刷新查蓉,在一個(gè)事件循環(huán)(Event Loop)內(nèi)的兩次數(shù)據(jù)變化會(huì)被合并射赛,例如你連續(xù)的先將節(jié)點(diǎn)內(nèi)容從A-B,B-A,React會(huì)認(rèn)為A變成B奶是,然后又從B變成A UI不發(fā)生任何變化楣责,而如果通過手動(dòng)控制竣灌,這種邏輯通常是極其復(fù)雜的。
盡管每一次都需要構(gòu)造完整的虛擬DOM樹秆麸,但是因?yàn)樘摂MDOM是內(nèi)存數(shù)據(jù)初嘹,性能是極高的,部而對實(shí)際DOM進(jìn)行操作的僅僅是Diff分沮趣,因而能達(dá)到提高性能的目的屯烦。這樣,在保證性能的同時(shí)房铭,開發(fā)者將不再需要關(guān)注某個(gè)數(shù)據(jù)的變化如何更新到一個(gè)或多個(gè)具體的DOM元素驻龟,而只需要關(guān)心在任意一個(gè)數(shù)據(jù)狀態(tài)下,整個(gè)界面是如何Render的缸匪。數(shù)據(jù)驅(qū)動(dòng)翁狐,聲明式
React的特點(diǎn)和優(yōu)勢
- 虛擬DOM 我們以前操作dom的方式是通過document.getElementById()的方式,這樣的過程實(shí)際上是先去讀取html的dom結(jié)構(gòu)凌蔬,將結(jié)構(gòu)轉(zhuǎn)換成變量露懒,再進(jìn)行操作 而reactjs定義了一套變量形式的dom模型,一切操作和換算直接在變量中砂心,這樣減少了操作真實(shí)dom懈词,性能真實(shí)相當(dāng)?shù)母哂止伲椭髁鱉VC框架有本質(zhì)的區(qū)別赃蛛,并不和dom打交道
- 組件系統(tǒng) react最核心的思想是將頁面中任何一個(gè)區(qū)域或者元素都可以看做一個(gè)組件 component 那么什么是組件呢操禀? 組件指的就是同時(shí)包含了html讶请、css撼班、js永淌、image元素的聚合體使用react開發(fā)的核心就是將頁面拆分成若干個(gè)組件粪般,并且react一個(gè)組件中同時(shí)耦合了css察纯、js秧秉、image褐桌,這種模式整個(gè)顛覆了過去的傳統(tǒng)的方式
- 單向數(shù)據(jù)流 其實(shí)reactjs的核心內(nèi)容就是數(shù)據(jù)綁定,所謂數(shù)據(jù)綁定指的是只要將一些服務(wù)端的數(shù)據(jù)和前端頁面綁定好象迎,開發(fā)者只關(guān)注實(shí)現(xiàn)業(yè)務(wù)就行了
- JSX 語法 在vue中荧嵌,我們使用render函數(shù)來構(gòu)建組件的dom結(jié)構(gòu)性能較高,因?yàn)槭∪チ瞬檎液途幾g模板的過程砾淌,但是在render中利用createElement創(chuàng)建結(jié)構(gòu)的時(shí)候代碼可讀性較低啦撮,較為復(fù)雜,此時(shí)可以利用jsx語法來在render中創(chuàng)建dom汪厨,解決這個(gè)問題赃春,但是前提是需要使用工具來編譯jsx
創(chuàng)建第一個(gè)組件
react開發(fā)需要引入多個(gè)依賴文件:react.js、react-dom.js劫乱,分別又有開發(fā)版本和生成版本
在這里一開始织中,我們先學(xué)習(xí)es5的組件寫法锥涕,React.createClass,需要引入的是15+
react.js中有React對象狭吼,幫助我們創(chuàng)建組件等功能
react-dom.js中有ReactDOM對象层坠,渲染組件的虛擬dom為真實(shí)dom的爆發(fā)功能
在編寫react代碼的時(shí)候會(huì)大量的使用到j(luò)sx代碼,但是需要編譯:
- 瀏覽器端編譯刁笙,通過引入browser破花、babel等對引入的script內(nèi)的代碼做編譯
- 利用webpack等開發(fā)環(huán)境進(jìn)行編譯,將編譯好的文件引入到應(yīng)用中
//創(chuàng)建組件
var Hello = React.createClass({
render:function () {
//render函數(shù)和Vue組件里的render完全一樣疲吸,在vue組件中可以不用編寫render函數(shù)座每,
//這個(gè)時(shí)候可以使用template模板來編寫組件的虛擬dom結(jié)構(gòu),
//然后vue組件會(huì)自動(dòng)講模板compile成虛擬dom結(jié)構(gòu)放入到render中執(zhí)行摘悴,但是react需要編寫render函數(shù)
return (
//jsx語法
<div>asdasd</div>
)
}
})
//利用ReactDOM對象的render方法將組件渲染到某個(gè)節(jié)點(diǎn)里
ReactDOM.render(<Hello/>,document.getElementById("app"))
組件是通過React.createClass創(chuàng)建的(ES5)峭梳,在es6中直接通過class關(guān)鍵字來創(chuàng)建
組件其實(shí)就是一個(gè)構(gòu)造器,每次使用組件都相當(dāng)于在實(shí)例化組件
react的組件必須使用render函數(shù)來創(chuàng)建組件的虛擬dom結(jié)構(gòu)
組件需要使用ReactDOM.render方法將其掛載在某一個(gè)節(jié)點(diǎn)上
組件的首字母必須大寫
JSX語法糖
JSX是一種語法,全稱:javascript xml
JSX語法不是必須使用的烦租,但是因?yàn)槭褂昧薐SX語法之后會(huì)降低我們的開發(fā)難度延赌,故而這樣的語法又被成為語法糖
在不使用JSX的時(shí)候除盏,需要使用React.createElement來創(chuàng)建組件的dom結(jié)構(gòu)叉橱,但是這樣的寫法雖然不需要編譯,但是維護(hù)和開發(fā)的難度很高者蠕,且可讀性很差
var world = React.createElement('h1',{className:'abc',id:'haha'},[
React.createElement('span',null,'Hello'),
React.createElement('mark',null,'React')
])
//利用ReactDOM對象的render方法將組件渲染到某個(gè)節(jié)點(diǎn)里
ReactDOM.render(world,document.getElementById("app1"))
及時(shí)使用了JSX語法了之后窃祝,也是需要將其編譯成原生的createElement的
JSX就是在js中使用的xml,但是踱侣,這里的xml不是真正的xml粪小,只能借鑒了一些xml的語法,例如:
最外層必須有根節(jié)點(diǎn)抡句、標(biāo)簽必須閉合
jsx借鑒xml的語法而不是html的語法原因:xml要比html嚴(yán)謹(jǐn)探膊,編譯更方便
組件dom添加樣式
在react里表達(dá)式的符號是 "{ }",作用和vue的表達(dá)式作用是一樣的
想給虛擬dom添加行內(nèi)樣式,需要使用表達(dá)式傳入樣式對象的方式來實(shí)現(xiàn):
<p style = { {color:'red',fontSize:2+'em'} }>Hello world</p>
行內(nèi)樣式需要寫入一個(gè)樣式對象待榔,而這個(gè)樣式對象的位置可以放在很多地方逞壁,例如React.createClass的配置項(xiàng)中、render函數(shù)里锐锣、組件原型上腌闯、外鏈js文件中
React推薦我們使用行內(nèi)樣式,因?yàn)閞eact覺得每一個(gè)組件都是一個(gè)獨(dú)立的整體
其實(shí)我們大多數(shù)情況下還是大量的在為元素添加類名雕憔、id以使用某些樣式姿骏,但是需要注意的是,class需要寫成className(因?yàn)楫吘故窃趯戭恓s代碼斤彼,會(huì)收到j(luò)s規(guī)則的現(xiàn)在分瘦,而class是關(guān)鍵字)
<p className="bg-p" id="myp" style = { this.style }>Hello world</p>
React Event
在react中蘸泻,我們想要給組件的dom添加事件的話,也是 需要在行內(nèi)添加的方式擅腰,事件名字需要寫成小駝峰的方式蟋恬,值利用表達(dá)式傳入一個(gè)函數(shù)即可
注意,在沒有渲染的時(shí)候趁冈,頁面中沒有真實(shí)dom歼争,所以是獲取不到dom的
給虛擬dom結(jié)構(gòu)中的節(jié)點(diǎn)添加樣式。在行內(nèi)添加,寫成駝峰形式渗勘,值是一個(gè)函數(shù)名沐绒,需要用{}包裹
handleClick:function () {
alert(1)
},
render:function () {
return (
<div>
<button onClick = {this.handleClick} className="click-btn">click</button>
<button onDoubleClick = {this.handleClick} className="click-btn">click</button>
</div>
)
}
組件嵌套
將一個(gè)組件渲染到某一個(gè)節(jié)點(diǎn)里的時(shí)候,會(huì)將這個(gè)節(jié)點(diǎn)里原有內(nèi)容覆蓋
組件嵌套的方式就是將子組件寫入到父組件的模板中去旺坠,且react沒有Vue中的內(nèi)容分發(fā)機(jī)制(slot)乔遮,所以我們在一個(gè)組件的模板中只能看到父子關(guān)系
var Hello = React.createClass({
render(){
return (
<h1>
Hello
<World></World>
</h1>
)
}
})
var World = React.createClass({
render(){
return (
<mark>
World-<Person/>
</mark>
)
}
})
//無狀態(tài)組件
var Person =function(){
return (<mark>lilei</mark>)
}
ReactDOM.render(<Hello/>,app)
注意,react中jsx里的注釋要寫成{/* */}的方式
React中的數(shù)據(jù)承載-Props/State
數(shù)據(jù)驅(qū)動(dòng)取刃、聲明式渲染:
任意的視圖變化都應(yīng)該由數(shù)據(jù)來控制
//$(".a").html(0)
var num = 0
function renderNum () { $(".a").html(num) }
React也是基于數(shù)據(jù)驅(qū)動(dòng)(聲明式)的框架蹋肮,組件中必然需要承載一些數(shù)據(jù),在react中起到這個(gè)作用的是屬性和狀態(tài)(props & state)
- 屬性(props) 在組件外部傳入璧疗,或者內(nèi)部設(shè)置坯辩,組件內(nèi)部通過this.props獲得
- 狀態(tài)(state) 在組件內(nèi)部設(shè)置或者更改,組件內(nèi)部通過this.state獲得
屬性(props)
屬性一般是外部傳入的崩侠,組件內(nèi)部也可以通過一些方式來初始化的設(shè)置漆魔,屬性不能被組件自己更改
屬性是描述性質(zhì)、特點(diǎn)的却音,組件自己不能隨意更改
使組件擁有屬性的方式:
- 在裝載(mount)組件的時(shí)候給組件傳入
傳入數(shù)據(jù)的時(shí)候改抡,除了字符串類型,其他的都應(yīng)該包上表達(dá)式系瓢,但是為了規(guī)整阿纤,所有的數(shù)據(jù)傳遞,最好都包上{}
var Gouzi = React.createClass({
render(){
console.log(this)
return (
<div>
<p>我的名字:{this.props.name}</p>
<p>我的性別:{this.props.sex}</p>
<p>我的年齡:{this.props.age}</p>
<p>我的父親是:{this.props.father}</p>
</div>
)
}
})
let info = {
sex:'male',
father:'狗爸'
}
ReactDOM.render(<Gouzi {...info} name={"大狗子"} age={26}/>,app)
- 父組件給子組件傳入
父組件在嵌套子組件的時(shí)候?yàn)樽咏M件傳入夷陋,傳入的方式和上面的方式一樣
//父組件的render函數(shù)
render(){
return (
<div>
<p>父組件:</p>
<hr/>
<Son name={'大狗子'}/>
<Son name={'二狗子'}/>
</div>
)
}
- 子組件自己設(shè)置
子組件可以通過getDefaultProps來設(shè)置默認(rèn)的屬性
getDefaultProps的值是函數(shù)欠拾,這個(gè)函數(shù)會(huì)返回一個(gè)對象,我們在這里對象里為組件設(shè)置默認(rèn)屬性
這種方式設(shè)置的屬性優(yōu)先級低肌稻,會(huì)被外部傳入的屬性值所覆蓋
getDefaultProps:function () {
console.log('getDefaultProps')
return {
name:'狗爸',
sonname:'二狗子'
}
},
//render
<p>我是{this.props.sonname}的父親-{this.props.name}</p>
根據(jù)屬性或狀態(tài)清蚀,我們可以在render中的表達(dá)式里做一些邏輯判斷,可以使用||爹谭、三元表達(dá)式枷邪、子執(zhí)行函數(shù)等等
getName(){
return this.props.name || '野狗子'
},
render:function () {
let {name} = this.props
return (
<div>
<p>我是子組件-{this.props.name || '野狗子'}</p>
<p>我是子組件-{this.props.name?this.props.name:'野狗子'}</p>
<p>我是子組件-{this.getName()}</p>
<p>我是子組件-{(function (obj) {
return obj.props.name || '野狗子'
})(this)}</p>
</div>
)
}