React框架大筆記

React框架學(xué)習(xí)

React的起源和發(fā)展

起初facebook在建設(shè)instagram(圖片分享)的時(shí)候嘞,因?yàn)闋砍兜揭粋€(gè)東東叫數(shù)據(jù)流,那為了處理數(shù)據(jù)流并且還要考慮好性能方面的問題嘞,F(xiàn)acebook開始對(duì)市場(chǎng)上的各種前端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ù)雜的用戶界面上(dom操作)。而來自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)了前端界面的 高性能 (減少dom操作 虛擬dom)高效率(組件開發(fā)模塊) 開發(fā)。

知乎 脈脈 it職場(chǎng)? 趣店? 滴滴

3年前端

14 html css? ajax? js jq? ? ? ios? 安卓? php? java? 手游藍(lán)海。。。 服務(wù)器渲染? 1階段? ie6的兼容問題

15 h5? 移動(dòng)端? 響應(yīng)式布局bootstrap? swiper 微場(chǎng)景? (angular 1.0)? ? ios? 安卓 php? java? 手游

16 前端工程化開發(fā) 剛剛開始興起? (angular vue? react)? ? ? ? ? ? ? 大前端? 微信小程序? 按照 php? java 手游 前端

17 前端框架大范圍的推廣 前端在開發(fā)中地位越來越高? 前后端分離? ? ? ? ? 大數(shù)據(jù)? 區(qū)款連? 人工智能ai

18 區(qū)款連? go? 前端框架大范圍的推廣快速的發(fā)展

React與傳統(tǒng)MVC的關(guān)系

輕量級(jí)的視圖層框架!

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í)就需要對(duì)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)行對(duì)比,得到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ù),性能是極高的,部而對(duì)實(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)勢(shì)

虛擬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ù)母?,和主流MVC框架有本質(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 5. 官網(wǎng)?https://reactjs.org/?6. 主流的 hb wb atom sub vscode xcode nopad++

創(chuàng)建第一個(gè)組件

react開發(fā)需要引入多個(gè)依賴文件:react.js、react-dom.js,分別又有開發(fā)版本和生成版本

在這里一開始,我們先學(xué)習(xí)es5的組件寫法,React.createClass,需要引入的是15+

react.js中有React對(duì)象,幫助我們創(chuàng)建組件等功能

react-dom.js中有ReactDOM對(duì)象,渲染組件的虛擬dom為真實(shí)dom的爆發(fā)功能

在編寫react代碼的時(shí)候會(huì)大量的使用到j(luò)sx代碼,但是需要編譯:

瀏覽器端編譯,通過引入browser、babel等對(duì)引入的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對(duì)象的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對(duì)象的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á)式的符號(hào)是 "{ }",作用和vue的表達(dá)式作用是一樣的

想給虛擬dom添加行內(nèi)樣式,需要使用表達(dá)式傳入樣式對(duì)象的方式來實(shí)現(xiàn):

<p style = { {color:'red',fontSize:2+'em'} }>Hello world</p>

行內(nèi)樣式需要寫入一個(gè)樣式對(duì)象,而這個(gè)樣式對(duì)象的位置可以放在很多地方,例如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),所以我們?cè)谝粋€(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è)對(duì)象,我們?cè)谶@里對(duì)象里為組件設(shè)置默認(rèn)屬性

這種方式設(shè)置的屬性優(yōu)先級(jí)低,會(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>

? ? )

}

狀態(tài)(state)

狀態(tài)就是組件描述某種顯示情況的數(shù)據(jù),由組件自己設(shè)置和更改,也就是說由組件自己維護(hù),使用狀態(tài)的目的就是為了在不同的狀態(tài)下使組件的顯示不同(自己管理)

在組件中只能通過getInitialState的鉤子函數(shù)來給組件掛載初始狀態(tài),在組件內(nèi)部通過this.state獲取

this.props和this.state是純js對(duì)象,在vue中,$data屬性是利用Object.defineProperty處理過的,更改$data的數(shù)據(jù)的時(shí)候會(huì)觸發(fā)數(shù)據(jù)的getter和setter,但是react中沒有做這樣的處理,如果直接更改的話,react是無法得知的,所以,需要使用特殊的更改狀態(tài)的方法:

setState(params)

在setState中傳入一個(gè)對(duì)象,就會(huì)將組件的狀態(tài)中鍵值對(duì)的部分更改,還可以傳入一個(gè)函數(shù),這個(gè)回調(diào)函數(shù)必須返回像上面方式一樣的一個(gè)對(duì)象,函數(shù)可以接收prevState和props

//1.

let doing = this.state.doing=='學(xué)習(xí)'+props.knowledge?'玩游戲':'學(xué)習(xí)'+props.knowledge

this.setState({doing})

//2.

this.setState((prevState,props)=>{

? ? return {

? ? ? ? doing:prevState.doing=='學(xué)習(xí)'+props.knowledge?'玩游戲':'學(xué)習(xí)'+props.knowledge

? ? }

})

實(shí)現(xiàn)下拉菜單的方式

通過數(shù)據(jù)來控制元素的行內(nèi)樣式中display的值,或者去控制類名

<ul style={{display:isMenuShow?'block':'none'}}><li>國(guó)內(nèi)新聞</li></ul>

...

<ul className={isMenuShow?'show':'hide'}><li>國(guó)內(nèi)新聞</li></ul>

根據(jù)數(shù)據(jù)控制是否渲染改節(jié)點(diǎn)、組件

{

? ? isMenuShow?<ul><li>國(guó)內(nèi)新聞</li></ul>:''

}

通過ref對(duì)dom、組件進(jìn)行標(biāo)記,在組件內(nèi)部通過this.refs獲取到之后,進(jìn)行操作

<ul ref='content'><li>國(guó)內(nèi)新聞</li></ul>

...

this.refs.content.style.display = this.state.isMenuShow?'block':'none'

屬性和狀態(tài)的對(duì)比

相似點(diǎn):都是純js對(duì)象,都會(huì)觸發(fā)render更新,都具有確定性(狀態(tài)/屬性相同,結(jié)果相同)

不同點(diǎn):

屬性能從父組件獲取,狀態(tài)不能

屬性可以由父組件修改,狀態(tài)不能

屬性能在內(nèi)部設(shè)置默認(rèn)值 ,狀態(tài)也可以

屬性不在組件內(nèi)部修改 ,狀態(tài)要改

屬性能設(shè)置子組件初始值 ,狀態(tài)不可以

屬性可以修改子組件的值,狀態(tài)不可以

狀態(tài)只和自己相關(guān),由自己維護(hù)

屬性不要自己修改,可以從父組件獲取,也可以給子組件設(shè)置

組件在運(yùn)行時(shí)自己需要修改的數(shù)據(jù)其實(shí)就是狀態(tài)而已

組件的生命周期

react中組件也有生命周期,也就是說也有很多鉤子函數(shù)供我們使用,下面是生命周期的圖示:

組件是一個(gè)構(gòu)造器,每一次使用組件都相當(dāng)于在實(shí)例化組件,在這個(gè)時(shí)候,組件就會(huì)經(jīng)歷一次生命周期,從實(shí)例化實(shí)例開始到這個(gè)實(shí)例銷毀的時(shí)候,都是一次完整的生命周期

組件的生命周期,我們會(huì)分為三個(gè)階段,初始化、運(yùn)行中、銷毀

初始化階段

實(shí)例化組件之后,組件的getDefaultProps鉤子函數(shù)會(huì)執(zhí)行

這個(gè)鉤子函數(shù)的目的是為組件的實(shí)例掛載默認(rèn)的屬性

這個(gè)鉤子函數(shù)只會(huì)執(zhí)行一次,也就是說,只在第一次實(shí)例化的時(shí)候執(zhí)行,創(chuàng)建出所有實(shí)例共享的默認(rèn)屬性,后面再實(shí)例化的時(shí)候,不會(huì)執(zhí)行g(shù)etDefaultProps,直接使用已有的共享的默認(rèn)屬性

理論上來說,寫成函數(shù)返回對(duì)象的方式,是為了防止實(shí)例共享,但是react專門為了讓實(shí)例共享,只能讓這個(gè)函數(shù)只執(zhí)行一次

組件間共享默認(rèn)屬性會(huì)減少內(nèi)存空間的浪費(fèi),而且也不需要擔(dān)心某一個(gè)實(shí)例更改屬性后其他的實(shí)例也會(huì)更改的問題,因?yàn)榻M件不能自己更改屬性,而且默認(rèn)屬性的優(yōu)先級(jí)低。

執(zhí)行g(shù)etInitialState為實(shí)例掛載初始狀態(tài),且每次實(shí)例化都會(huì)執(zhí)行,也就是說,每一個(gè)組件實(shí)例都擁有自己獨(dú)立的狀態(tài)呢

執(zhí)行componentWillMount,相當(dāng)于Vue里的created+beforeMount,這里是在渲染之前最后一次更改數(shù)據(jù)的機(jī)會(huì),在這里更改的話是不會(huì)觸發(fā)render的重新執(zhí)行

多做一些初始數(shù)據(jù)的獲取

執(zhí)行render,渲染dom

執(zhí)行componentDidMount ,相當(dāng)于Vue里的mounted,多用于操作真實(shí)dom

運(yùn)行中階段

當(dāng)組件mount到頁面中之后,就進(jìn)入了運(yùn)行中階段,在這里有5個(gè)鉤子函數(shù),但是這5個(gè)函數(shù)只有在數(shù)據(jù)(屬性、狀態(tài))發(fā)送改變的時(shí)候才會(huì)執(zhí)行

componentWillReceiveProps

當(dāng)父組件給子組件傳入的屬性改變的時(shí)候,子組件的這個(gè)函數(shù)才會(huì)執(zhí)行

當(dāng)執(zhí)行的時(shí)候,函數(shù)接收的參數(shù)是子組件接收到的新參數(shù),這個(gè)時(shí)候,新參數(shù)還沒有同步到this.props上,多用于判斷新屬性和原有屬性的變化后更改組件的狀態(tài)

接下來就會(huì)執(zhí)行shouldComponentUpdate,這個(gè)函數(shù)的作用:

當(dāng)屬性或狀態(tài)發(fā)送改變后控制組件是否要更新,提高性能,返回true就更新,否則不更新,默認(rèn)返回true

接收nextProp、nextState,根據(jù)根據(jù)新屬性狀態(tài)和原屬性狀態(tài)作出對(duì)比、判斷后控制是否更新

componentWillUpdate,在這里,組件馬上就要重新render了,多做一些準(zhǔn)備工作,千萬千萬,不要在這里修改狀態(tài),否則會(huì)死循環(huán) 相當(dāng)于Vue中的beforeUpdate

render,重新渲染dom

componentDidUpdate,在這里,新的dom結(jié)構(gòu)已經(jīng)誕生了,相當(dāng)于Vue里的updated

銷毀階段

當(dāng)組件被銷毀之前的一剎那,會(huì)觸發(fā)componentWillUnmount,臨死前的掙扎

相當(dāng)于Vue里的beforeDestroy,所以說一般會(huì)做一些擦屁股的事情

為什么Vue中有destroyed,而react卻沒有componentDidUnmount

Vue在調(diào)用$destroy方法的時(shí)候就會(huì)執(zhí)行beforeDestroy,然后組件被銷毀,這個(gè)時(shí)候組件的dom結(jié)構(gòu)還存在于頁面結(jié)構(gòu)中,也就說如果想要對(duì)殘留的dom結(jié)構(gòu)進(jìn)行處理必須在destroyed處理,但是react執(zhí)行完componentWillUnmount之后把事件、數(shù)據(jù)、dom都全部處理掉了,所以根本不需要其他的鉤子函數(shù)了

怎么樣就算組件被銷毀:

當(dāng)父組件從渲染這個(gè)子組件變成不渲染這個(gè)子組件的時(shí)候,子組件相當(dāng)于被銷毀

調(diào)用ReactDOM.unmountComponentAtNode(node) 方法來將某節(jié)點(diǎn)中的組件銷毀

React中的事件對(duì)象

react中對(duì)于事件進(jìn)行了處理,解決了一些兼容性問題,react事件對(duì)象上面掛載著nativeEvent,這個(gè)就是原生的事件對(duì)象

react對(duì)事件對(duì)象做了優(yōu)化,如果不取值的話,值都是null

React中組件通信方式

父組件與子組件通信

父組件將自己的狀態(tài)傳遞給子組件,子組件當(dāng)做屬性來接收,當(dāng)父組件更改自己狀態(tài)的時(shí)候,子組件接收到的屬性就會(huì)發(fā)生改變

父組件利用ref對(duì)子組件做標(biāo)記,通過調(diào)用子組件的方法以更改子組件的狀態(tài),也可以調(diào)用子組件的方法..

子組件與父組件通信

父組件將自己的某個(gè)方法傳遞給子組件,在方法里可以做任意操作,比如可以更改狀態(tài),子組件通過this.props接收到父組件的方法后調(diào)用。

兄弟組件通信

在react沒有類似vue中的事件總線來解決這個(gè)問題,我們只能借助它們共同的父級(jí)組件來實(shí)現(xiàn),將非父子關(guān)系裝換成多維度的父子關(guān)系

復(fù)雜的非父子組件通信在react中很難處理,多組件間的數(shù)據(jù)共享也不好處理,所以我們會(huì)使用flux、redux來實(shí)現(xiàn)這樣的功能,解決這個(gè)問題

React中表單元素默認(rèn)值

在react中,如果需要 給表單元素設(shè)置默認(rèn)value或者checked,需要設(shè)置成defaultValue/defaultChecked,否則設(shè)置默認(rèn)值以后,用戶無法更改

React中的mixins

在vue中我們可以將一些通用的、公用的方法放入到某一個(gè)純js對(duì)象中,然后,在需要使用改方法的組件中使用mixins配置(值為對(duì)象)將該js對(duì)象中的方法注入到組件中,這樣就能實(shí)現(xiàn)代碼復(fù)用,便于維護(hù)

在React中曾經(jīng)也有這樣的api,但是在高版本react中推薦我們使用es6中的class來創(chuàng)建組件了,這個(gè)時(shí)候無法使用mixinsapi,所以mixins被廢棄了,如果要使用公用代碼抽離,我們可以使用模塊化

React-keys

我們?cè)趓eact中循環(huán)列表數(shù)據(jù)的時(shí)候,需要對(duì)循環(huán)出來的虛擬jsx節(jié)點(diǎn)傳入上key這個(gè)數(shù)據(jù),

Keys可以在DOM中的某些元素被增加或刪除的時(shí)候幫助React識(shí)別哪些元素發(fā)生了變化。因此你應(yīng)當(dāng)給數(shù)組中的每一個(gè)元素賦予一個(gè)確定的標(biāo)識(shí)。

狀態(tài)提升

就是如果有多個(gè)組件共享一個(gè)數(shù)據(jù),把這個(gè)數(shù)據(jù)放到共同的父級(jí)組件中來管理

組合

在vue中有一個(gè)內(nèi)容分發(fā)叫slot,在react中也有實(shí)現(xiàn),就是可以在使用組件的時(shí)候,在組件標(biāo)簽內(nèi)部放入一些不固定的內(nèi)容,在該組件的模板中,只有{this.props.children}來表示

//App

<Dialog

close={this.ToggleDialogShow} isShow={isDialogShow}

>

? ? <ContentA/>

? ? <ContentA/>

? ? <ContentB/>

</Dialog>

//dialog

<div style={{display:isShow?'block':'none'}} className="dialog">

? ? <Button handler={this.props.close} text="關(guān)閉"/>?

? ? {this.props.children}//這里就是slot

</div>

webpack

前端工程化: npm、cnpm、yarn、bower | grunt 、 gulp 、webpack

gulp: 基于流的前端自動(dòng)化構(gòu)建工具,基于流的任務(wù)式的工具

webpack: 是一款模塊化打包工具,webpack是基于配置的,通過配置一些選項(xiàng)來讓webpack執(zhí)行打包任務(wù)。

npm i webpack -g

npm i webpack-cli -g (4.0+)

npm i yarn -g

webpack在打包的時(shí)候,依靠依賴關(guān)系圖,在打包的時(shí)候需要告知webpack兩個(gè)概念:入口和出口

一般情況下,我們需要使用webpack.config.js進(jìn)行配置

entry

entry配置項(xiàng)目打包的入口,值可以為單個(gè)的字符串執(zhí)行某一個(gè)文件的地址,這個(gè)時(shí)候該文件就是入口文件,webpack會(huì)根據(jù)入口文件里各模塊間的關(guān)系形成依賴關(guān)系圖,然后根據(jù)依賴關(guān)系圖進(jìn)行打包

entry:'./src/app.js',

output:{

? ? path:path.join(__dirname,'build'),

? ? filename:'app.js'

}

但是有的時(shí)候我們需要的是多入口,我們就寫成數(shù)組的形式,數(shù)組里的每一個(gè)字符串地址指向的都是一個(gè)獨(dú)立的入口,webpack會(huì)將這些入口的依賴打包

entry:['./src/app.js','./src/vendor.js'],

output:{

? ? path:path.join(__dirname,'build'),

? ? filename:'[name].js'//不確定名字的時(shí)候,這里會(huì)打包成main.js

}

剛才的兩種entry配置都只會(huì)打包出一個(gè)js文件,但是在某一個(gè)應(yīng)用中我們可能需要將js根據(jù)依賴關(guān)系打包成多個(gè)js文件,并且在多頁面應(yīng)用中,我們也確實(shí)不可能只使用一個(gè)js文件,那么我們就可以使用如下的配置:

? ? entry:{

? ? ? ? app:'./src/app.js',

? ? ? ? vendor:'./src/vendor.js'

? ? },

? ? output:{

? ? ? ? path:path.join(__dirname,'build'),

? ? ? ? filename:'[name]_[hash].js'

? ? }

這樣,因?yàn)閒ilename里寫成名字是[name],所以會(huì)根據(jù)entry的配置的鍵名來為打包出的js文件命名,hash是每次打包的一個(gè)隨機(jī)的hash值,可以用來做版本控制

output

在這里我們配置打包輸出的一些選項(xiàng)

filename可以確定打包出來的文件的名字,在里面我們可以使用[name],[hash]這樣的占位符

path配置打包出去的文件的路徑,需要是絕對(duì)路徑

env

在命令行或者終端中執(zhí)行 webpack --env hello命令,就相當(dāng)于在打包的時(shí)候傳入一個(gè)參數(shù)為hello

在webpack.config.js中可以暴露出一個(gè)函數(shù),這個(gè)函數(shù)就可以接收到env參數(shù),當(dāng)然函數(shù)就可以根據(jù)env參數(shù)來有選擇的返回某一個(gè)或多個(gè)配置對(duì)象

module.exports = (env)=>{

? ? if(env=='production'){

? ? ? ? return productionConfig

? ? }

? ? return developmentConfig

}

--watch 可以讓webpack去監(jiān)聽文件的改變。 可以在package.json里的scripts中配置一些快捷操作,通過npm run來運(yùn)行

plugins

在webpack編譯用的是loader,但是有一些loader無法完成的任務(wù),交由插件(plugin)來完成,插件的時(shí)候需要在配置項(xiàng)中配置plugins選項(xiàng),值是數(shù)組,可以放入多個(gè)插件的使用,而一般的插件都是一個(gè)構(gòu)造器,我們只需在plugins數(shù)組中放入該插件的實(shí)例即可,在實(shí)例化插件的時(shí)候又可以傳入options,對(duì)插件的使用進(jìn)行配置

html-webpack-plugin

這個(gè)插件可以選擇是否依據(jù)模板來生成一個(gè)打包好的html文件,在里面可以配置、title、template、filename、minify等選項(xiàng),詳情請(qǐng)查閱文檔

在這個(gè)插件里,我們可以使用jade、hbs、ejs等模板引擎來編譯成html,這里舉例jade的配置:

文檔

npm i jade jade-loader --save-dev

module:{

? ? rules:[

? ? ? ? {

? ? ? ? ? ? test:/\.jade$/,

? ? ? ? ? ? use:'jade-loader'

? ? ? ? }

? ? ]

},

plugins:[

? ? new HtmlWebpackPlugin({

? ? ? ? // title:'webpack-config-demo',

? ? ? ? template:'./src/index.jade',

? ? ? ? filename:'index.html'

? ? })

]

webpack-dev-server

webpack相輔相成的有一個(gè)server功能工具可以提供開發(fā)的熱更新服務(wù)器

npm install webpack-dev-server -g npm install webpack-dev-server -D

第一種啟動(dòng)方式: 直接執(zhí)行webpack-dev-server,如果有需要配置的選項(xiàng),在后面跟上參數(shù)即可。例如

webpack-dev-server --hot true

第二種啟動(dòng)方式:在webpack.config.js中配置devServer的選項(xiàng),執(zhí)行webpack-dev-server就ok

devServer:{

? ? port:9000,

? ? contentBase:'./build',

? ? historyApiFallback: true,

? ? open: true,

? ? proxy:{


? ? }

}

LOADERS

在webpack中專門有一些東西用來編譯文件、處理文件,這些東西就叫l(wèi)oader,loader的使用就是在配置項(xiàng)中,設(shè)置modules,在modules中設(shè)置rule值為數(shù)組,在數(shù)組里放入多個(gè)匹配規(guī)則:

module:{

? ? rules:[

? ? ? ? {test:/\.css$/,use:'css-loader'}

? ? ],

? ? //before

? ? loaders:[

? ? ? ? {test:/\.css$/,loader:'css-loader'}

? ? ],

}

test為此次匹配要匹配的文件正則規(guī)則,use代表要使用的loader

使用url-loader可以將css中引入的圖片(背景圖)、js中生成的img圖片處理一下,生成到打包目錄里

視圖html-withimg-loader可以將html中img標(biāo)簽引入的img圖片打包到打包目錄

file-loader

{

? ? test:/\.(png|jpe?g|svg|gif)$/,

? ? // use:'url-loader?limit=1000&name=images/[hash:8].[name].[ext]'

? ? use:[

? ? ? ? {

? ? ? ? ? ? loader:'url-loader',

? ? ? ? ? ? options:{

? ? ? ? ? ? ? ? limit:1000,

? ? ? ? ? ? ? ? name:'/static/images/assets/[hash:8].[name].[ext]'

? ? ? ? ? ? }

? ? ? ? }

? ? ]

},

{

? ? test:/\.html$/,

? ? use:'html-withimg-loader'

}

處理css:

cnpm i css-loader style-loader --save-dev

配置:

{ test:/.css$/, use:['style-loader','css-loader'] }

注意。webpack中l(wèi)oader的使用是從后往前的

css-loader可以將引入到j(luò)s中的css代碼給抽離出來,style-loader可以將抽離出來的css代碼放入到style標(biāo)簽中

處理sass

{ test:/.scss$/, use:['style-loader','css-loader','sass-loader'] },

將引入項(xiàng)目的css文件、scss文件抽成一個(gè)文件,引入到頁面中

cnpm i extract-text-webpack-plugin

const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')

///loader

{

test:/\.css$/,

use:ExtractTextWebpackPlugin.extract({

? ? ? fallback: "style-loader",

? ? ? use: "css-loader"

? ? })

},

{

test:/\.scss/,

use:ExtractTextWebpackPlugin.extract({

? ? ? fallback: "style-loader",

? ? ? use: ["css-loader","sass-loader"]

? ? })

}

///plugin

new ExtractTextWebpackPlugin({

filename:'app.css',

allChunks:true

})

因?yàn)镋xtractTextWebpackPlugin對(duì)webpack4支持的不是很好,所以我們這樣解決:

cnpm i extract-text-webpack-plugin@next -D yarn add extract-text-webpack-plugin@next -D

@next下載的就是最最新的版本,可能是開發(fā)版本

webpack-dev-server進(jìn)行了一個(gè)優(yōu)化,在跑起服務(wù)的時(shí)候,會(huì)將編譯結(jié)果保存在內(nèi)存里,不會(huì)實(shí)時(shí)的輸出的打包結(jié)果

css兼容優(yōu)化處理:post-css 、autoprefixer

處理es6:

需要的依賴:

"babel": "^6.23.0", "babel-core": "^6.24.1", "babel-loader": "^7.0.0", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1",

rules: { test:/.js$/, exclude: /node_modules/, loader:'babel-loader', query: { presets: ['es2015','react'] } }

ES6中的react

1.創(chuàng)建組件:

使用class來創(chuàng)建組件

class App extends React.Component {

}

2.默認(rèn)狀態(tài)的設(shè)置

在es6中不再使用getInitialState來設(shè)置默認(rèn)狀態(tài),而是在constructor里面直接給this.state上掛載狀態(tài)

class App extends Component { constructor(props){ super(props)

this.state={

doing:'吃飯'

}

}

}

默認(rèn)屬性的設(shè)置

在es6中,通過給類設(shè)置defaultProps屬性來設(shè)置默認(rèn)屬性

App.defaultProps = { name:'App根組件' }

做屬性傳參驗(yàn)證

import PropTypes from 'prop-types';

App.propTypes = { name:PropTypes.string }

5.鉤子函數(shù)有變化

getDefaultProps、getInitialState沒有了

多出了constructor,而這個(gè)函數(shù)本身是類的構(gòu)造器,在這里相當(dāng)于getDefaultProps、getInitialState的結(jié)合

create-react-app 腳手架

npm install creat-react-app -g

create-react-app my-app //生成一個(gè)react開發(fā)模板在my-app目錄 //生成的過程特別緩慢,可以使用yarn工具來下載,也就是說先去下載安裝yarn :npm install yarn -g

當(dāng)我們要進(jìn)行二次配置的時(shí)候,需要找到node_modules文件夾里的react-scripts進(jìn)行配置,但是當(dāng)我們執(zhí)行npm run eject就可以將配置文件抽出,方便開發(fā)配置

無狀態(tài)組件

當(dāng)我們使用某些組件的時(shí)候,發(fā)現(xiàn),該組件不需要擁有自己的狀態(tài),只需要接收到外界傳入的屬性之后做出相應(yīng)的反應(yīng)即可

這樣的話,我們可以利用純函數(shù)的方式將其制作成無狀態(tài)組件,提高性能

import React from 'react'

const Button = (props)=>{

return <button onClick={props.handler}>我要花錢</button>

}

export default Button

Flux

在2014年,facebook提出了Flux,F(xiàn)lux 是一種架構(gòu)思想,專門解決軟件的結(jié)構(gòu)問題。它跟MVC 架構(gòu)是同一類東西,但是更加簡(jiǎn)單和清晰。

其實(shí)FLUX在react里的應(yīng)用就類似于vue中的vuex的作用,但是

在vue中,vue是完整的mvvm框架,而vuex只是一個(gè)全局的插件

react只是一個(gè)視圖層的框架,在flux是一個(gè)架構(gòu)思想,我們?cè)谧鲰?xiàng)目的時(shí)候使用flux架構(gòu)的話要比單純使用react要簡(jiǎn)單很多,這個(gè)時(shí)候,react在整個(gè)FLUX架構(gòu)中擔(dān)任某一個(gè)角色的

react在這里只是充當(dāng)了FLUX架構(gòu)體系中的view層

Flux的組成部分:

View: 視圖層

ActionCreator(動(dòng)作創(chuàng)造者):視圖層發(fā)出的消息(比如mouseClick)

Dispatcher(派發(fā)器):用來接收Actions、執(zhí)行回調(diào)函數(shù)

Store(數(shù)據(jù)層):用來存放應(yīng)用的狀態(tài),一旦發(fā)生變動(dòng),就提醒Views要更新頁面

Flux的流程:

組件獲取到store中保存的數(shù)據(jù)掛載在自己的狀態(tài)上

用戶產(chǎn)生了操作,調(diào)用actions的方法

actions接收到了用戶的操作,進(jìn)行一系列的邏輯代碼、異步操作

然后actions會(huì)創(chuàng)建出對(duì)應(yīng)的action,action帶有標(biāo)識(shí)性的屬性

actions調(diào)用dispatcher的dispatch方法將action傳遞給dispatcher

dispatcher接收到action并根據(jù)標(biāo)識(shí)信息判斷之后,調(diào)用store的更改數(shù)據(jù)的方法

store的方法被調(diào)用后,更改狀態(tài),并觸發(fā)自己的某一個(gè)事件

store更改狀態(tài)后事件被觸發(fā),該事件的處理程序會(huì)通知view去獲取最新的數(shù)據(jù)

redux

React 只是 DOM 的一個(gè)抽象層,并不是 Web 應(yīng)用的完整解決方案。有兩個(gè)方面,它沒涉及。

代碼結(jié)構(gòu)

組件之間的通信

2014年 Facebook 提出了 Flux 架構(gòu)的概念,引發(fā)了很多的實(shí)現(xiàn)。2015年,Redux 出現(xiàn),將 Flux 與函數(shù)式編程結(jié)合一起,很短時(shí)間內(nèi)就成為了最熱門的前端架構(gòu)。

如果你不知道是否需要 Redux,那就是不需要它

只有遇到 React 實(shí)在解決不了的問題,你才需要 Redux

簡(jiǎn)單說,如果你的UI層非常簡(jiǎn)單,沒有很多互動(dòng),Redux 就是不必要的,用了反而增加復(fù)雜性。

用戶的使用方式非常簡(jiǎn)單

用戶之間沒有協(xié)作

不需要與服務(wù)器大量交互,也沒有使用 WebSocket

視圖層(View)只從單一來源獲取數(shù)據(jù)

需要使用redux的項(xiàng)目:

用戶的使用方式復(fù)雜

不同身份的用戶有不同的使用方式(比如普通用戶和管理員)

多個(gè)用戶之間可以協(xié)作

與服務(wù)器大量交互,或者使用了WebSocket

View要從多個(gè)來源獲取數(shù)據(jù)

從組件層面考慮,什么樣子的需要redux:

某個(gè)組件的狀態(tài),需要共享

某個(gè)狀態(tài)需要在任何地方都可以拿到

一個(gè)組件需要改變?nèi)譅顟B(tài)

一個(gè)組件需要改變另一個(gè)組件的狀態(tài)

redux的設(shè)計(jì)思想:

Web 應(yīng)用是一個(gè)狀態(tài)機(jī),視圖與狀態(tài)是一一對(duì)應(yīng)的。

所有的狀態(tài),保存在一個(gè)對(duì)象里面(唯一數(shù)據(jù)源)。

redux的流程:

1.store通過reducer創(chuàng)建了初始狀態(tài) 2.view通過store.getState()獲取到了store中保存的state掛載在了自己的狀態(tài)上 3.用戶產(chǎn)生了操作,調(diào)用了actions 的方法 4.actions的方法被調(diào)用,創(chuàng)建了帶有標(biāo)示性信息的action 5.actions將action通過調(diào)用store.dispatch方法發(fā)送到了reducer中 6.reducer接收到action并根據(jù)標(biāo)識(shí)信息判斷之后返回了新的state 7.store的state被reducer更改為新state的時(shí)候,store.subscribe方法里的回調(diào)函數(shù)會(huì)執(zhí)行,此時(shí)就可以通知view去重新獲取state

注意:flux、redux都不是必須和react搭配使用的,因?yàn)閒lux和redux是完整的架構(gòu),在學(xué)習(xí)react的時(shí)候,只是將react的組件作為redux中的視圖層去使用了。

reducer必須是一個(gè)純函數(shù):

Reducer 函數(shù)最重要的特征是,它是一個(gè)純函數(shù)。也就是說,只要是同樣的輸入,必定得到同樣的輸出。

純函數(shù)是函數(shù)式編程的概念,必須遵守以下一些約束。

不得改寫參數(shù)

不能調(diào)用系統(tǒng) I/O 的API

不能調(diào)用Date.now()或者M(jìn)ath.random()等不純的方法,因?yàn)槊看螘?huì)得到不一樣的結(jié)果

由于 Reducer 是純函數(shù),就可以保證同樣的State,必定得到同樣的 View。但也正因?yàn)檫@一點(diǎn),Reducer 函數(shù)里面不能改變 State,必須返回一個(gè)全新的對(duì)象,請(qǐng)參考下面的寫法。

// State 是一個(gè)對(duì)象

function reducer(state, action) {

? return Object.assign({}, state, { thingToChange });

? // 或者

? return { ...state, ...newState };

}

// State 是一個(gè)數(shù)組

function reducer(state, action) {

? return [...state, newItem];

}

最好把 State 對(duì)象設(shè)成只讀。你沒法改變它,要得到新的 State,唯一辦法就是生成一個(gè)新對(duì)象。這樣的好處是,任何時(shí)候,與某個(gè) View 對(duì)應(yīng)的 State 總是一個(gè)不變的對(duì)象。

我們可以通過在createStore中傳入第二個(gè)參數(shù)來設(shè)置默認(rèn)的state,但是這種形式只適合于只有一個(gè)reducer的時(shí)候

劃分reducer

因?yàn)橐粋€(gè)應(yīng)用中只能有一個(gè)大的state,這樣的話reducer中的代碼將會(huì)特別特別的多,那么就可以使用combineReducers方法將已經(jīng)分開的reducer合并到一起

注意:

分離reducer的時(shí)候,每一個(gè)reducer維護(hù)的狀態(tài)都應(yīng)該不同

通過store.getState獲取到的數(shù)據(jù)也是會(huì)安裝reducers去劃分的

劃分多個(gè)reducer的時(shí)候,默認(rèn)狀態(tài)只能創(chuàng)建在reducer中,因?yàn)閯澐謗educer的目的,就是為了讓每一個(gè)reducer都去獨(dú)立管理一部分狀態(tài)

React-router

市場(chǎng)上的react-router的版本有1、2、3、4,1-3的差別不大,使用于16.0.0以下的版本

react-router 4.0 適用于16.0.0以上

在這里使用15.6.1的react。這個(gè)版本的react允許使用React.createClass來創(chuàng)建組件,在16以上只能使用class類的方式來創(chuàng)建

渲染根組件的時(shí)候,最外層包裹上Router組件,在其上可以設(shè)置history屬性,值可以是hashHistory||browserHistory

當(dāng)值為hashHistory的時(shí)候,url的變化為hash值的變化,router會(huì)去檢測(cè)hash變化來實(shí)現(xiàn)組件的切換

當(dāng)值為browserHistory的時(shí)候,url的變化為path的變化,需要后端進(jìn)行配置

Router中使用Route組件來描述每一級(jí)路由,Route上有path、component屬性,代表著當(dāng)path改變成...的時(shí)候,就渲染..組件

在需要切換路由組件的地方,通過this.props.children來表示對(duì)應(yīng)路由組件

在Route中可以多次嵌套R(shí)oute來實(shí)現(xiàn)多級(jí)路由

IndexRoute可以設(shè)置該路由中的默認(rèn)子路由

IndexRedirect可以設(shè)置在進(jìn)入該路由之后馬上跳轉(zhuǎn)到哪里

使用Redirect組件可以做到從一個(gè)路由馬上重定向到其他路由,利用這樣的屬性,當(dāng)我們form設(shè)置為'*'的時(shí)候,就可以將匹配不到的路由重定向到某げ路由下

可以在配置Route的時(shí)候給path里加入/:param 才表示此路由需要參數(shù)

傳入的時(shí)候,querystring參數(shù)可以在Link里的query中傳入和設(shè)置,在目標(biāo)組件中,通過this.props中的,params、routePrams、location等來接收參數(shù)

可以通過過Router傳入routes參數(shù),值為數(shù)組,來設(shè)置路由配置:

const routeConfig = [

? { path: '/',

? ? component: App,

? ? indexRoute: { component: Home },

? ? childRoutes: [

? ? ? { path: 'home', component: Home },

? ? ? { path: 'news',

? ? ? ? component: News,

? ? ? ? childRoutes: [

? ? ? ? ? { path: 'inside', component: Inside },

? ? ? ? ? { path: 'outside',component:Outside}

? ? ? ? ]

? ? ? },

? ? ? { path: 'detail/:id', component: Detail },

? ? ? {path:'*',component:Home}

? ? ]

? }

]

ReactDOM.render(

<Router routes={routeConfig} history={hashHistory}></Router>

,document.getElementById('app'))

編程式導(dǎo)航

在路由組件中通過this.props.history獲取到history對(duì)象,利用里面push、replace、go、goBack方法來進(jìn)行隱式跳轉(zhuǎn)

可以從react-router中引入browserHistory或者h(yuǎn)ashHistory調(diào)用里面的push、replace、go、goBack方法來進(jìn)行隱式跳轉(zhuǎn)

可以通過在路由配置上設(shè)置 onLeave和onEnter路由鉤子來監(jiān)聽路由的變化

UI組件庫(kù)

關(guān)于React的UI組件庫(kù)市場(chǎng)上也有很多,在這里我們使用螞蟻金服開發(fā)的AntDesign組件庫(kù)

這是PC端的,移動(dòng)端的是Antd-Mobile

React-redux

這個(gè)庫(kù)或者說工具是redux的開發(fā)者專門為react創(chuàng)建出來的,為我們?cè)趓eact中使用redux提供便利

起到的是橋梁的作用,能將react和redux更好的連接在一起

React-Redux 將所有組件分成兩大類:UI 組件/木偶組件(presentational component)和容器組件/智能組件(container component)。

UI 組件有以下幾個(gè)特征。

只負(fù)責(zé) UI 的呈現(xiàn),不帶有任何業(yè)務(wù)邏輯

沒有狀態(tài)(即不使用this.state這個(gè)變量)

所有數(shù)據(jù)都由參數(shù)(this.props)提供

不使用任何 Redux 的 API

容器組件的特征恰恰相反。

負(fù)責(zé)管理數(shù)據(jù)和業(yè)務(wù)邏輯,不負(fù)責(zé) UI 的呈現(xiàn)

帶有內(nèi)部狀態(tài)

使用 Redux 的 API

只要記住一句話就可以了:UI 組件負(fù)責(zé) UI 的呈現(xiàn),容器組件負(fù)責(zé)管理數(shù)據(jù)和邏輯。

你可能會(huì)問,如果一個(gè)組件既有 UI 又有業(yè)務(wù)邏輯,那怎么辦?回答是,將它拆分成下面的結(jié)構(gòu):外面是一個(gè)容器組件,里面包了一個(gè)UI 組件。前者負(fù)責(zé)與外部的通信,將數(shù)據(jù)傳給后者,由后者渲染出視圖。

React-Redux 規(guī)定,所有的 UI 組件都由用戶提供,容器組件則是由 React-Redux 自動(dòng)生成。也就是說,用戶負(fù)責(zé)視覺層,狀態(tài)管理則是全部交給它。

使用方法及步驟:

使用Provider組件,包裹在應(yīng)用的最外層,并為Provider注入store屬性,此時(shí),Provider就會(huì)將自己的store屬性傳遞給子組件組合中的容器組件

使用connect函數(shù),可以根據(jù)一個(gè)現(xiàn)有的UI組件生成一個(gè)容器組件,且我們?cè)谑褂玫臅r(shí)候,其實(shí)一直在使用的都是容器組件,connect函數(shù)執(zhí)行之后返回一個(gè)函數(shù),將返回的函數(shù)傳入U(xiǎn)I組件并執(zhí)行之后就會(huì)生成一個(gè)容器組件

connect函數(shù)有兩個(gè)參數(shù):mapStateToProps,mapDispatchToProps

mapStateToProps的作用很簡(jiǎn)單,就是將redux中的state傳遞到UI組件的props上,此參數(shù)是一個(gè)函數(shù),接收到store的state然后再返回一個(gè)對(duì)象,返回的對(duì)象中的屬性就會(huì)傳遞到UI組件的屬性上

mapStateToProps對(duì)store進(jìn)行了訂閱,只要state更改,mapStateToProps會(huì)自動(dòng)執(zhí)行并獲取到最新的state傳入到UI組件的屬性上

mapDispatchToprops 函數(shù),接收到dispatch參數(shù),其實(shí)就是store.dispatch,返回的對(duì)象中設(shè)置的方法可以使用到dispatch,且能傳入到UI組件的屬性上

那么,有了mapDistpatchToProps之后,我們就不需要actions了嗎?

我們需要將一些復(fù)雜的業(yè)務(wù)邏輯,或者說異步的業(yè)務(wù)邏輯抽離出來放入到actions里面去,也就是后所mapDispatchToProps里自己創(chuàng)建的只是一些簡(jiǎn)單的方法就可以了

第一次使用react-redux等工具的做法

創(chuàng)建了actionCreator,專門生成action,又設(shè)置 了actions,在actions里放一些異步的、復(fù)雜的操作之后,調(diào)用actionCreator生成action再dispatch到reducer

其實(shí)我們上面創(chuàng)建actions的目的,就是因?yàn)锳ctionCreator不能做復(fù)雜的動(dòng)作,其實(shí)我們可以使用redux-thunk來對(duì)reducer創(chuàng)建中間件,讓actionCreator的方法能返回一個(gè)函數(shù),這個(gè)函數(shù)就可以接收到dispatch,且做出異步操作之后dispatch出action,也就是說,我們不需要再創(chuàng)建actions來分離異步復(fù)雜操作,而且直接可以在ActionCreator里寫異步方法

步驟:

對(duì)store中的reducer使用redux-thunk

import {createStore,applyMiddleware} from 'redux'

import reducer from './reducer'

import thunk from 'redux-thunk'

const store = createStore(reducer,applyMiddleware(thunk))

export default store

在ActionCreator的方法中返回方法來做異步處理

const actions_thunk = {

clearCart(){

//做異步操作之后生成action且dispatch

return (dispatch)=>{

setTimeout(function(){

localStorage.removeItem('cars')

let action = change_cars([])

dispatch(action)

},500)

}

}

}

可以將actionCreator的方法利用bindActionCreator放入到mapDispatchToProps中

import {bindActionCreators} from 'redux'

import actions_thunk from '../../redux/ActionCreators/actions_thunk'

export default connect(state=>state,dispatch=>{

return {

actions_thunk:bindActionCreators(actions_thunk,dispatch)

}

})(ClearCar)

///

button onClick={this.props.actions_thunk.clearCart}

其實(shí),現(xiàn)在有這樣的流行做法:

將所有的數(shù)據(jù)都交由redux管理,這樣的話,我們的組件在UI層的邏輯就更純粹了,而且可以做到數(shù)據(jù)緩存,比如,A組件獲取了數(shù)據(jù)放入到redux中,當(dāng)A組件被切換掉之后重新切換回來的時(shí)候,數(shù)據(jù)依然在redux中可以找到,也就是說直接取出來用就ok,不需要重新獲取

React 擴(kuò)展

ref推薦使用函數(shù)的方法:

字符串方式

<Son ref="son"></Son>

//

this.refs.son

函數(shù)方式(推薦)

<Son ref={(el)=>{this.son = el}}></Son>

//

this.son

考試題:

react的特點(diǎn)不包括什么?

聲明式設(shè)計(jì)、高效、靈活、(雙向數(shù)據(jù)流)

動(dòng)畫可以使用哪個(gè)第三方插件實(shí)現(xiàn):

(ReactTransitionGroup)/animate.css/transitionTranslate/redux-thunk

ReactRouter中,路由的onLeave應(yīng)該寫在哪里:

路由對(duì)應(yīng)的組件中、父組件中、(路由組件中)、最外層大組件中

react-redux中的connect方法的返回值是一個(gè): ContainerComponent = connect()(UIComponent) 對(duì)象、(容器組件)、UI組件、數(shù)組

react中常提到的中間件的概念,指的是:

react中間件、flux中間件、(redux中間件)、react-redux中間件

redux設(shè)計(jì)的三大原則:

(store唯一/唯一數(shù)據(jù)源、 state只讀、 reducer是純函數(shù)) reducer只讀

哪些不是react-router的組件:

(Provider)、Route、Router、(MapStateToProps)

下面哪些方法可以使componentWillUpdate執(zhí)行

屬性更改或者狀態(tài)更改

請(qǐng)簡(jiǎn)述對(duì)虛擬dom的理解,為什么使用虛擬DOM可以極大的提升react的性能

虛擬dom是真實(shí)dom的js對(duì)象映射,使用虛擬dom,避免對(duì)原生dom的創(chuàng)建和比對(duì),取而代之的創(chuàng)建和比對(duì)的是js對(duì)象

原生dom的創(chuàng)建和比對(duì)是非常消耗性能的,而js對(duì)象的對(duì)比和創(chuàng)建對(duì)性能開銷很小,從這種方式來提供應(yīng)用的性能

請(qǐng)說明在react中ref的作用,并寫出使用ref的兩種方式,說明哪一種是官方推薦的

ref可以使我們?cè)趓eact對(duì)dom或者子組件做出標(biāo)記并獲?。?/p>

//this.refs.son <Son ref={(el)=>{this.son = el}}>//this.son(官方推薦)

說明react中,父子組件項(xiàng)目傳值的方式,并說明在大型項(xiàng)目中為什么要引入flux或者redux這種開發(fā)架構(gòu) 父組件將自己的狀態(tài)當(dāng)成屬性傳遞給子組件 父組件將自己的方法傳遞給子組件,子組件在調(diào)用的時(shí)候傳參 父組件通過ref獲取到子組件,調(diào)用子組件的方法傳參

react是一款視圖層的輕量級(jí)前端框架,大量的非父子組件通信、狀態(tài)共享會(huì)導(dǎo)致整個(gè)項(xiàng)目數(shù)據(jù)復(fù)雜,難以維護(hù),所以react不適合處理大量的數(shù)據(jù)通信,為了解決這個(gè)問題,引入了FLUX、REDUX這樣的數(shù)據(jù)架構(gòu),react結(jié)合FLUX或者redux才能實(shí)現(xiàn)比較復(fù)雜的前端項(xiàng)目

在react中,列表循環(huán)盡量不要使用index作為key值,這和diff算法有關(guān)系,請(qǐng)簡(jiǎn)述diff算法中key值有什么作用,為什么key中使用index值會(huì)降低代碼性能

key值是diff算法中對(duì)比兩個(gè)虛擬dom的最重要參考,決定了哪些列表中的組件可以復(fù)用,如果使用index作為key中,列表數(shù)據(jù)改變后,會(huì)導(dǎo)致同一個(gè)dom元素的key中發(fā)送改變,本來可以復(fù)用的組件必須重新創(chuàng)建,降低頁面性能,除非列表不需要改變,一般情況不使用index作為key值

請(qǐng)列舉你所了解的react中的性能優(yōu)化

沒必要存在store中的數(shù)據(jù),存在state中就可以

函數(shù)的this執(zhí)行放在constructor中改變

和頁面顯示無關(guān)的數(shù)據(jù)不要放在state中

shouldComponentUpdate來判斷組件是否需要重新render(使用pureComponent)

使用性能分析工具進(jìn)行性能分析,找問題解決問題

13.1 pureComponnet pureComponnet里如果接收到的新屬性或者是更改后的狀態(tài)和原屬性、原狀態(tài)相同想等的話,就不會(huì)去重新render了 在里面也可以使用shouldComponentUpdate,而且。是否重新渲染以shouldComponentUpdate的返回值為最終的決定因素 class ABC extends pureComponnet {

}

請(qǐng)說明react中引入redux-thunk、redux-promise這兩種中間件可以解決什么樣的問題

通常情況下,action只是一個(gè)對(duì)象,不能包含異步操作,這導(dǎo)致了很多創(chuàng)建action的邏輯只能寫在組件中,代碼量較多也不便于復(fù)用,同時(shí)對(duì)該部分代碼測(cè)試的時(shí)候也比較困難,組件的業(yè)務(wù)邏輯也不清晰,使用中間件了之后,可以通過actionCreator異步編寫action,這樣代碼就會(huì)拆分到actionCreator中,可維護(hù)性大大提高,可以方便于測(cè)試、復(fù)用,同時(shí)actionCreator還集成了異步操作中不同的action派發(fā)機(jī)制,減少編碼過程中的代碼量

畫圖說明redux的架構(gòu),寫出redux中常用的函數(shù)

dispatch , subscribe,combineReducers,getState,createStore

簡(jiǎn)述react-redux結(jié)合react-router在項(xiàng)目中的使用方式

創(chuàng)建store,編寫reducer

在外面嵌套,將store傳遞給各個(gè)子組件

編寫UI組件

使用react-redux的connect方法結(jié)合mapStateToProps、mapDispatchToProps生成容器組件,容器組件和store連接在一起

router4

創(chuàng)建store,編寫reducer

在外面嵌套,將store傳遞給各個(gè)子組件

編寫UI組件

使用react-redux的connect方法結(jié)合mapStateToProps、mapDispatchToProps生成容器組件,容器組件和store連接在一起

如果某一個(gè)組件不是路由組件,卻需要使用router相關(guān)api,并且還需要使用store中的state的時(shí)候,需要在最外層包裹withRouter,里面再使用connect生成容器組件

下列說法錯(cuò)誤的是:

(React是一款專注于數(shù)據(jù)層的前端框架)、react中需要調(diào)用setState方法來重置狀態(tài)、react中的虛擬dom可以提升框架自身的性能、(react是一款符合MVVM設(shè)計(jì)思想的前端框架)

關(guān)于前端組件化說法錯(cuò)誤的是:

前端組件使得復(fù)雜的代碼得以被更好的劃分、組件化的代碼設(shè)計(jì)方式增加了代碼的可復(fù)用性、(在拆分組件的時(shí)候?qū)⒔M件拆分的越小越細(xì)越好)、組件化開發(fā)是一種設(shè)計(jì)思想,不是react獨(dú)享的

在react中,異步獲取ajax數(shù)據(jù)一般放在那個(gè)生命周期函數(shù):componentWillMount

使用es6定義組件,可以在那個(gè)生命周期鉤子函數(shù)里使用this.state=state 對(duì)state進(jìn)行賦值。而不需要調(diào)用this.setState方法:constructor

在redux中,重要的組成部分不包括:

store、action、reducer、(dispatcher)

webpack中html-webpack-plugin插件可以完成的任務(wù)是:

(在打包輸出目錄中自動(dòng)生成index.html)、(向打包目錄中的index.html文件內(nèi)插入打包生成的js文件引用)、將js源碼中引用的css代碼抽離ちゅ單獨(dú)的css文件并放置到打包輸出目錄、(像打包輸出目錄中的index.html文件插入打包生成的css引用)

關(guān)于jsx,說明正確的是:(ad)

a:jsx中可以通過{}來使用js的表達(dá)式,b:jsx中可以通過{}來使用js的代碼,c:jsx中可以使用style={color:'red'}來設(shè)置元素的樣式、d:jsx代碼會(huì)被解析成一個(gè)js對(duì)象

react組件被掛載到頁面 的時(shí)候,被執(zhí)行的生命周期函數(shù)包括:(ab)

a: componentWillMount,b:render,c:componentDidUpdate,d:shouldComponentUpdate

在自定義的react組件中,哪些生命周期函數(shù)可以不寫(acd)

a: constructor b:render c:componentWillMount d:componentWillUnmount

說法正確的是:(ab)

a: 父組件通過屬性的方式給子組件傳值,b:子組件通過props接收父組件的值,c:state中和頁面無關(guān)的屬性發(fā)送變化時(shí),render不會(huì)執(zhí)行,d:shouldComponentUpdate函數(shù)的兩個(gè)參數(shù)分別是當(dāng)前的state和當(dāng)前的props

在react組件中,當(dāng)(props或者state)發(fā)送變化的時(shí)候,會(huì)導(dǎo)致render生命周期函數(shù)重新執(zhí)行

使用react-redux時(shí),頁面上的組件需要被拆分成(容器)組件和(UI)組件,通過使用(connect/mapStateToProps)方法,可以把store中固定state內(nèi)容映射到組件上

使用ES6創(chuàng)建react組件的方式是(class ... extends React.Component),ES5創(chuàng)建組件的方法是(React.createClass),創(chuàng)建無狀態(tài)組件的方式是(function(props){return ()})

react中,ref屬性的作用是(獲取jsx中元素的真實(shí)dom節(jié)點(diǎn)或子組件)

es5語法創(chuàng)建react組件比es6多了兩個(gè)生命周期函數(shù)(getDefaultProps/getInitialState)

請(qǐng)簡(jiǎn)述react-router中hashHistory和browserHistory的區(qū)別:

這是react-router中設(shè)置監(jiān)聽url路徑變化的兩種方式,hashHistory在切換路由的時(shí)候會(huì)在url上跟著哈希值,browserHistory通過判斷path變化才切換路由,且path變化的時(shí)候后端可以接收到請(qǐng)求,需要后端配置忽略掉

請(qǐng)畫圖說明flux中的單向數(shù)據(jù)流

用戶訪問View-》view發(fā)出用戶的action-》dispatcher收到action要求store進(jìn)行更新-》store更改后,觸發(fā)一個(gè)事件-》view接收到該事件的觸發(fā),更新頁面

簡(jiǎn)述react-redux的用法

創(chuàng)建store、reducer,通過provider將store傳遞給各個(gè)子組件,創(chuàng)建ui組件,生成容器組件,利用connect將store和容器組件連接

說明redux設(shè)計(jì)和使用的三大原則:

唯一數(shù)據(jù)源、保持狀態(tài)只讀、數(shù)據(jù)改變只能通過純函數(shù)完成

說明redux-thunk的使用方法:

npm install redux-thunk -S

//store import {createStore,applyMiddleware} from 'redux' import thunk from 'redux-thunk'

import reducer from './reducer'

const store = createStore(reducer,applyMiddleware(thunk))

//actionCreator

const actionCreator = { handlerChange(){ return (dispatch)=>{

? ? ? ? ...

? ? }

}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容