React框架學習
起初facebook在建設instagram(圖片分享)的時候嘞叼风,因為牽扯到一個東東叫數(shù)據(jù)流,那為了處理數(shù)據(jù)流并且還要考慮好性能方面的問題嘞半沽,F(xiàn)acebook開始對市場上的各種前端MVC框架去進行一個研究砂代,然而并沒有看上眼的,于是Facebook覺得布讹,還是自己開發(fā)一個才是最棒的琳拭,那么他們決定拋開很多所謂的“最佳實踐”,重新思考前端界面的構(gòu)建方式描验,他們就自己開發(fā)了一套白嘁,果然大牛創(chuàng)造力還是很強大的。
基于HTML的前端界面開發(fā)正變得越來越復雜膘流,其本質(zhì)問題基本都可以歸結(jié)于如何將來自于服務器端或者用戶輸入的動態(tài)數(shù)據(jù)高效的反映到復雜的用戶界面上(dom操作)絮缅。而來自Facebook的React框架正是完全面向此問題的一個解決方案,按官網(wǎng)描述呼股,其出發(fā)點為:用于開發(fā)數(shù)據(jù)不斷變化的大型應用程序(Building large applications with data that changes over time)耕魄。相比傳統(tǒng)型的前端開發(fā),React開辟了一個相當另類的途徑彭谁,實現(xiàn)了前端界面的 高性能 (減少dom操作 虛擬dom)高效率(組件開發(fā)模塊) 開發(fā)吸奴。
知乎 脈脈 it職場? 趣店? 滴滴
3年前端
14 html css? ajax? js jq? ? ? ios? 安卓? php? java? 手游藍海。缠局。则奥。 服務器渲染? 1階段? ie6的兼容問題
15 h5? 移動端? 響應式布局bootstrap? swiper 微場景? (angular 1.0)? ? ios? 安卓 php? java? 手游
16 前端工程化開發(fā) 剛剛開始興起? (angular vue? react)? ? ? ? ? ? ? 大前端? 微信小程序? 按照 php? java 手游 前端
17 前端框架大范圍的推廣 前端在開發(fā)中地位越來越高? 前后端分離? ? ? ? ? 大數(shù)據(jù)? 區(qū)款連? 人工智能ai
18 區(qū)款連? go? 前端框架大范圍的推廣快速的發(fā)展
輕量級的視圖層框架!
React不是一個完整的MVC框架狭园,最多可以認為是MVC中的V(View)读处,甚至React并不非常認可MVC開發(fā)模式;
React高性能的原理:
在Web開發(fā)中我們總需要將變化的數(shù)據(jù)實時反應到UI上唱矛,這時就需要對DOM進行操作档泽。而復雜或頻繁的DOM操作通常是性能瓶頸產(chǎn)生的原因(如何進行高性能的復雜DOM操作通常是衡量一個前端開發(fā)人員技能的重要指標)。
React為此引入了虛擬DOM(Virtual DOM)的機制:在瀏覽器端用Javascript實現(xiàn)了一套DOM API揖赴」菽洌基于React進行開發(fā)時所有的DOM構(gòu)造都是通過虛擬DOM進行,每當數(shù)據(jù)變化時燥滑,React都會重新構(gòu)建整個DOM樹渐北,然后React將當前整個DOM樹和上一次的DOM樹進行對比,得到DOM結(jié)構(gòu)的區(qū)別铭拧,然后僅僅將需要變化的部分進行實際的瀏覽器DOM更新赃蛛。而且React能夠批處理虛擬DOM的刷新恃锉,在一個事件循環(huán)(Event Loop)內(nèi)的兩次數(shù)據(jù)變化會被合并,例如你連續(xù)的先將節(jié)點內(nèi)容從A-B,B-A呕臂,React會認為A變成B破托,然后又從B變成A UI不發(fā)生任何變化,而如果通過手動控制歧蒋,這種邏輯通常是極其復雜的土砂。
盡管每一次都需要構(gòu)造完整的虛擬DOM樹,但是因為虛擬DOM是內(nèi)存數(shù)據(jù)谜洽,性能是極高的萝映,部而對實際DOM進行操作的僅僅是Diff分,因而能達到提高性能的目的阐虚。這樣序臂,在保證性能的同時,開發(fā)者將不再需要關注某個數(shù)據(jù)的變化如何更新到一個或多個具體的DOM元素实束,而只需要關心在任意一個數(shù)據(jù)狀態(tài)下奥秆,整個界面是如何Render的。數(shù)據(jù)驅(qū)動咸灿,聲明式
虛擬DOM
我們以前操作dom的方式是通過document.getElementById()的方式吭练,這樣的過程實際上是先去讀取html的dom結(jié)構(gòu),將結(jié)構(gòu)轉(zhuǎn)換成變量析显,再進行操作
而reactjs定義了一套變量形式的dom模型鲫咽,一切操作和換算直接在變量中,這樣減少了操作真實dom谷异,性能真實相當?shù)母叻质椭髁鱉VC框架有本質(zhì)的區(qū)別,并不和dom打交道
組件系統(tǒng)
react最核心的思想是將頁面中任何一個區(qū)域或者元素都可以看做一個組件 component
那么什么是組件呢歹嘹?
組件指的就是同時包含了html箩绍、css、js尺上、image元素的聚合體
使用react開發(fā)的核心就是將頁面拆分成若干個組件材蛛,并且react一個組件中同時耦合了css、js怎抛、image卑吭,這種模式整個顛覆了過去的傳統(tǒng)的方式
單向數(shù)據(jù)流
其實reactjs的核心內(nèi)容就是數(shù)據(jù)綁定,所謂數(shù)據(jù)綁定指的是只要將一些服務端的數(shù)據(jù)和前端頁面綁定好马绝,開發(fā)者只關注實現(xiàn)業(yè)務就行了
JSX 語法
在vue中豆赏,我們使用render函數(shù)來構(gòu)建組件的dom結(jié)構(gòu)性能較高,因為省去了查找和編譯模板的過程,但是在render中利用createElement創(chuàng)建結(jié)構(gòu)的時候代碼可讀性較低掷邦,較為復雜白胀,此時可以利用jsx語法來在render中創(chuàng)建dom,解決這個問題抚岗,但是前提是需要使用工具來編譯jsx 5. 官網(wǎng)?https://reactjs.org/?6. 主流的 hb wb atom sub vscode xcode nopad++
react開發(fā)需要引入多個依賴文件:react.js或杠、react-dom.js,分別又有開發(fā)版本和生成版本
在這里一開始宣蔚,我們先學習es5的組件寫法向抢,React.createClass,需要引入的是15+
react.js中有React對象件已,幫助我們創(chuàng)建組件等功能
react-dom.js中有ReactDOM對象笋额,渲染組件的虛擬dom為真實dom的爆發(fā)功能
在編寫react代碼的時候會大量的使用到jsx代碼元暴,但是需要編譯:
瀏覽器端編譯篷扩,通過引入browser、babel等對引入的script內(nèi)的代碼做編譯
利用webpack等開發(fā)環(huán)境進行編譯茉盏,將編譯好的文件引入到應用中
? ? //創(chuàng)建組件
? ? var Hello = React.createClass({
? ? ? ? render:function () {
? ? ? ? ? ? //render函數(shù)和Vue組件里的render完全一樣鉴未,在vue組件中可以不用編寫render函數(shù),這個時候可以使用template模板來編寫組件的虛擬dom結(jié)構(gòu)鸠姨,然后vue組件會自動講模板compile成虛擬dom結(jié)構(gòu)放入到render中執(zhí)行铜秆,但是react需要編寫render函數(shù)
? ? ? ? ? ? return (
? ? ? ? ? ? ? ? //jsx語法
? ? ? ? ? ? ? ? <div>asdasd</div>
? ? ? ? ? ? )
? ? ? ? }
? ? })
? ? //利用ReactDOM對象的render方法將組件渲染到某個節(jié)點里
? ? ReactDOM.render(<Hello/>,document.getElementById("app"))
組件是通過React.createClass創(chuàng)建的(ES5),在es6中直接通過class關鍵字來創(chuàng)建
組件其實就是一個構(gòu)造器,每次使用組件都相當于在實例化組件
react的組件必須使用render函數(shù)來創(chuàng)建組件的虛擬dom結(jié)構(gòu)
組件需要使用ReactDOM.render方法將其掛載在某一個節(jié)點上
組件的首字母必須大寫
JSX是一種語法讶迁,全稱:javascript xml
JSX語法不是必須使用的连茧,但是因為使用了JSX語法之后會降低我們的開發(fā)難度,故而這樣的語法又被成為語法糖
在不使用JSX的時候巍糯,需要使用React.createElement來創(chuàng)建組件的dom結(jié)構(gòu)啸驯,但是這樣的寫法雖然不需要編譯,但是維護和開發(fā)的難度很高祟峦,且可讀性很差
var world = React.createElement('h1',{className:'abc',id:'haha'},[
? ? React.createElement('span',null,'Hello'),
? ? React.createElement('mark',null,'React')
])
//利用ReactDOM對象的render方法將組件渲染到某個節(jié)點里
ReactDOM.render(world,document.getElementById("app1"))
及時使用了JSX語法了之后罚斗,也是需要將其編譯成原生的createElement的
JSX就是在js中使用的xml,但是宅楞,這里的xml不是真正的xml针姿,只能借鑒了一些xml的語法,例如:
最外層必須有根節(jié)點厌衙、標簽必須閉合
jsx借鑒xml的語法而不是html的語法原因:xml要比html嚴謹距淫,編譯更方便
在react里表達式的符號是 "{ }",作用和vue的表達式作用是一樣的
想給虛擬dom添加行內(nèi)樣式,需要使用表達式傳入樣式對象的方式來實現(xiàn):
<p style = { {color:'red',fontSize:2+'em'} }>Hello world</p>
行內(nèi)樣式需要寫入一個樣式對象婶希,而這個樣式對象的位置可以放在很多地方溉愁,例如React.createClass的配置項中、render函數(shù)里、組件原型上拐揭、外鏈js文件中
React推薦我們使用行內(nèi)樣式撤蟆,因為react覺得每一個組件都是一個獨立的整體
其實我們大多數(shù)情況下還是大量的在為元素添加類名、id以使用某些樣式堂污,但是需要注意的是家肯,class需要寫成className(因為畢竟是在寫類js代碼,會收到js規(guī)則的現(xiàn)在盟猖,而class是關鍵字)
<p className="bg-p" id="myp" style = { this.style }>Hello world</p>
在react中讨衣,我們想要給組件的dom添加事件的話,也是 需要在行內(nèi)添加的方式式镐,事件名字需要寫成小駝峰的方式反镇,值利用表達式傳入一個函數(shù)即可
注意,在沒有渲染的時候娘汞,頁面中沒有真實dom歹茶,所以是獲取不到dom的
給虛擬dom結(jié)構(gòu)中的節(jié)點添加樣式。在行內(nèi)添加,寫成駝峰形式你弦,值是一個函數(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>
? ? )
}
將一個組件渲染到某一個節(jié)點里的時候,會將這個節(jié)點里原有內(nèi)容覆蓋
組件嵌套的方式就是將子組件寫入到父組件的模板中去禽作,且react沒有Vue中的內(nèi)容分發(fā)機制(slot)尸昧,所以我們在一個組件的模板中只能看到父子關系
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ū)動旷偿、聲明式渲染:
任意的視圖變化都應該由數(shù)據(jù)來控制
//$(".a").html(0)
var num = 0 function renderNum () { $(".a").html(num) }
React也是基于數(shù)據(jù)驅(qū)動(聲明式)的框架烹俗,組件中必然需要承載一些數(shù)據(jù),在react中起到這個作用的是屬性和狀態(tài)(props & state)
屬性(props) 在組件外部傳入萍程,或者內(nèi)部設置幢妄,組件內(nèi)部通過this.props獲得
狀態(tài)(state) 在組件內(nèi)部設置或者更改,組件內(nèi)部通過this.state獲得
屬性一般是外部傳入的尘喝,組件內(nèi)部也可以通過一些方式來初始化的設置磁浇,屬性不能被組件自己更改
屬性是描述性質(zhì)、特點的朽褪,組件自己不能隨意更改
使組件擁有屬性的方式:
在裝載(mount)組件的時候給組件傳入
傳入數(shù)據(jù)的時候置吓,除了字符串類型,其他的都應該包上表達式缔赠,但是為了規(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)
父組件給子組件傳入
父組件在嵌套子組件的時候為子組件傳入嗤堰,傳入的方式和上面的方式一樣
//父組件的render函數(shù)
render(){
? ? return (
? ? ? ? <div>
? ? ? ? ? ? <p>父組件:</p>
? ? ? ? ? ? <hr/>
? ? ? ? ? ? <Son name={'大狗子'}/>
? ? ? ? ? ? <Son name={'二狗子'}/>
? ? ? ? </div>
? ? )
}
子組件自己設置
子組件可以通過getDefaultProps來設置默認的屬性
getDefaultProps的值是函數(shù)戴质,這個函數(shù)會返回一個對象,我們在這里對象里為組件設置默認屬性
這種方式設置的屬性優(yōu)先級低,會被外部傳入的屬性值所覆蓋
getDefaultProps:function () {
? ? console.log('getDefaultProps')
? ? return {
? ? ? ? name:'狗爸',
? ? ? ? sonname:'二狗子'
? ? }
},
//render
<p>我是{this.props.sonname}的父親-{this.props.name}</p>
根據(jù)屬性或狀態(tài)告匠,我們可以在render中的表達式里做一些邏輯判斷戈抄,可以使用||、三元表達式后专、子執(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)就是組件描述某種顯示情況的數(shù)據(jù)划鸽,由組件自己設置和更改,也就是說由組件自己維護戚哎,使用狀態(tài)的目的就是為了在不同的狀態(tài)下使組件的顯示不同(自己管理)
在組件中只能通過getInitialState的鉤子函數(shù)來給組件掛載初始狀態(tài),在組件內(nèi)部通過this.state獲取
this.props和this.state是純js對象,在vue中裸诽,$data屬性是利用Object.defineProperty處理過的,更改$data的數(shù)據(jù)的時候會觸發(fā)數(shù)據(jù)的getter和setter型凳,但是react中沒有做這樣的處理丈冬,如果直接更改的話,react是無法得知的甘畅,所以埂蕊,需要使用特殊的更改狀態(tài)的方法:
setState(params)
在setState中傳入一個對象,就會將組件的狀態(tài)中鍵值對的部分更改橄浓,還可以傳入一個函數(shù)粒梦,這個回調(diào)函數(shù)必須返回像上面方式一樣的一個對象亮航,函數(shù)可以接收prevState和props
//1.
let doing = this.state.doing=='學習'+props.knowledge?'玩游戲':'學習'+props.knowledge
this.setState({doing})
//2.
this.setState((prevState,props)=>{
? ? return {
? ? ? ? doing:prevState.doing=='學習'+props.knowledge?'玩游戲':'學習'+props.knowledge
? ? }
})
通過數(shù)據(jù)來控制元素的行內(nèi)樣式中display的值荸实,或者去控制類名
<ul style={{display:isMenuShow?'block':'none'}}><li>國內(nèi)新聞</li></ul>
...
<ul className={isMenuShow?'show':'hide'}><li>國內(nèi)新聞</li></ul>
根據(jù)數(shù)據(jù)控制是否渲染改節(jié)點、組件
{
? ? isMenuShow?<ul><li>國內(nèi)新聞</li></ul>:''
}
通過ref對dom缴淋、組件進行標記准给,在組件內(nèi)部通過this.refs獲取到之后,進行操作
<ul ref='content'><li>國內(nèi)新聞</li></ul>
...
this.refs.content.style.display = this.state.isMenuShow?'block':'none'
相似點:都是純js對象重抖,都會觸發(fā)render更新露氮,都具有確定性(狀態(tài)/屬性相同,結(jié)果相同)
不同點:
屬性能從父組件獲取钟沛,狀態(tài)不能
屬性可以由父組件修改畔规,狀態(tài)不能
屬性能在內(nèi)部設置默認值 ,狀態(tài)也可以
屬性不在組件內(nèi)部修改 恨统,狀態(tài)要改
屬性能設置子組件初始值 叁扫,狀態(tài)不可以
屬性可以修改子組件的值,狀態(tài)不可以
狀態(tài)只和自己相關畜埋,由自己維護
屬性不要自己修改莫绣,可以從父組件獲取,也可以給子組件設置
組件在運行時自己需要修改的數(shù)據(jù)其實就是狀態(tài)而已
react中組件也有生命周期悠鞍,也就是說也有很多鉤子函數(shù)供我們使用对室,下面是生命周期的圖示:
組件是一個構(gòu)造器,每一次使用組件都相當于在實例化組件,在這個時候掩宜,組件就會經(jīng)歷一次生命周期蔫骂,從實例化實例開始到這個實例銷毀的時候,都是一次完整的生命周期
組件的生命周期牺汤,我們會分為三個階段纠吴,初始化、運行中慧瘤、銷毀
實例化組件之后戴已,組件的getDefaultProps鉤子函數(shù)會執(zhí)行
這個鉤子函數(shù)的目的是為組件的實例掛載默認的屬性
這個鉤子函數(shù)只會執(zhí)行一次,也就是說锅减,只在第一次實例化的時候執(zhí)行糖儡,創(chuàng)建出所有實例共享的默認屬性,后面再實例化的時候怔匣,不會執(zhí)行getDefaultProps确买,直接使用已有的共享的默認屬性
理論上來說弯予,寫成函數(shù)返回對象的方式,是為了防止實例共享,但是react專門為了讓實例共享曲秉,只能讓這個函數(shù)只執(zhí)行一次
組件間共享默認屬性會減少內(nèi)存空間的浪費,而且也不需要擔心某一個實例更改屬性后其他的實例也會更改的問題覆旭,因為組件不能自己更改屬性策州,而且默認屬性的優(yōu)先級低。
執(zhí)行getInitialState為實例掛載初始狀態(tài)浓利,且每次實例化都會執(zhí)行挤庇,也就是說,每一個組件實例都擁有自己獨立的狀態(tài)呢
執(zhí)行componentWillMount贷掖,相當于Vue里的created+beforeMount嫡秕,這里是在渲染之前最后一次更改數(shù)據(jù)的機會,在這里更改的話是不會觸發(fā)render的重新執(zhí)行
多做一些初始數(shù)據(jù)的獲取
執(zhí)行render苹威,渲染dom
執(zhí)行componentDidMount 昆咽,相當于Vue里的mounted,多用于操作真實dom
當組件mount到頁面中之后,就進入了運行中階段牙甫,在這里有5個鉤子函數(shù)掷酗,但是這5個函數(shù)只有在數(shù)據(jù)(屬性、狀態(tài))發(fā)送改變的時候才會執(zhí)行
componentWillReceiveProps
當父組件給子組件傳入的屬性改變的時候腹暖,子組件的這個函數(shù)才會執(zhí)行
當執(zhí)行的時候汇在,函數(shù)接收的參數(shù)是子組件接收到的新參數(shù),這個時候脏答,新參數(shù)還沒有同步到this.props上,多用于判斷新屬性和原有屬性的變化后更改組件的狀態(tài)
接下來就會執(zhí)行shouldComponentUpdate,這個函數(shù)的作用:
當屬性或狀態(tài)發(fā)送改變后控制組件是否要更新糕殉,提高性能,返回true就更新亩鬼,否則不更新,默認返回true
接收nextProp阿蝶、nextState雳锋,根據(jù)根據(jù)新屬性狀態(tài)和原屬性狀態(tài)作出對比、判斷后控制是否更新
componentWillUpdate,在這里羡洁,組件馬上就要重新render了玷过,多做一些準備工作,千萬千萬筑煮,不要在這里修改狀態(tài)辛蚊,否則會死循環(huán) 相當于Vue中的beforeUpdate
render,重新渲染dom
componentDidUpdate真仲,在這里袋马,新的dom結(jié)構(gòu)已經(jīng)誕生了,相當于Vue里的updated
當組件被銷毀之前的一剎那,會觸發(fā)componentWillUnmount秸应,臨死前的掙扎
相當于Vue里的beforeDestroy虑凛,所以說一般會做一些擦屁股的事情
為什么Vue中有destroyed,而react卻沒有componentDidUnmount
Vue在調(diào)用$destroy方法的時候就會執(zhí)行beforeDestroy软啼,然后組件被銷毀桑谍,這個時候組件的dom結(jié)構(gòu)還存在于頁面結(jié)構(gòu)中,也就說如果想要對殘留的dom結(jié)構(gòu)進行處理必須在destroyed處理祸挪,但是react執(zhí)行完componentWillUnmount之后把事件锣披、數(shù)據(jù)、dom都全部處理掉了匕积,所以根本不需要其他的鉤子函數(shù)了
怎么樣就算組件被銷毀:
當父組件從渲染這個子組件變成不渲染這個子組件的時候盈罐,子組件相當于被銷毀
調(diào)用ReactDOM.unmountComponentAtNode(node) 方法來將某節(jié)點中的組件銷毀
react中對于事件進行了處理榜跌,解決了一些兼容性問題闪唆,react事件對象上面掛載著nativeEvent,這個就是原生的事件對象
react對事件對象做了優(yōu)化钓葫,如果不取值的話悄蕾,值都是null
父組件與子組件通信
父組件將自己的狀態(tài)傳遞給子組件,子組件當做屬性來接收础浮,當父組件更改自己狀態(tài)的時候帆调,子組件接收到的屬性就會發(fā)生改變
父組件利用ref對子組件做標記,通過調(diào)用子組件的方法以更改子組件的狀態(tài),也可以調(diào)用子組件的方法..
子組件與父組件通信
父組件將自己的某個方法傳遞給子組件豆同,在方法里可以做任意操作番刊,比如可以更改狀態(tài),子組件通過this.props接收到父組件的方法后調(diào)用影锈。
兄弟組件通信
在react沒有類似vue中的事件總線來解決這個問題芹务,我們只能借助它們共同的父級組件來實現(xiàn)蝉绷,將非父子關系裝換成多維度的父子關系
復雜的非父子組件通信在react中很難處理,多組件間的數(shù)據(jù)共享也不好處理枣抱,所以我們會使用flux熔吗、redux來實現(xiàn)這樣的功能,解決這個問題
在react中佳晶,如果需要 給表單元素設置默認value或者checked桅狠,需要設置成defaultValue/defaultChecked,否則設置默認值以后轿秧,用戶無法更改
在vue中我們可以將一些通用的中跌、公用的方法放入到某一個純js對象中,然后菇篡,在需要使用改方法的組件中使用mixins配置(值為對象)將該js對象中的方法注入到組件中晒他,這樣就能實現(xiàn)代碼復用,便于維護
在React中曾經(jīng)也有這樣的api逸贾,但是在高版本react中推薦我們使用es6中的class來創(chuàng)建組件了陨仅,這個時候無法使用mixinsapi,所以mixins被廢棄了铝侵,如果要使用公用代碼抽離灼伤,我們可以使用模塊化
我們在react中循環(huán)列表數(shù)據(jù)的時候,需要對循環(huán)出來的虛擬jsx節(jié)點傳入上key這個數(shù)據(jù)咪鲜,
Keys可以在DOM中的某些元素被增加或刪除的時候幫助React識別哪些元素發(fā)生了變化狐赡。因此你應當給數(shù)組中的每一個元素賦予一個確定的標識。
就是如果有多個組件共享一個數(shù)據(jù)疟丙,把這個數(shù)據(jù)放到共同的父級組件中來管理
在vue中有一個內(nèi)容分發(fā)叫slot颖侄,在react中也有實現(xiàn),就是可以在使用組件的時候享郊,在組件標簽內(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="關閉"/>?
? ? {this.props.children}//這里就是slot
</div>
前端工程化: npm炊琉、cnpm展蒂、yarn、bower | grunt 苔咪、 gulp 锰悼、webpack
gulp: 基于流的前端自動化構(gòu)建工具,基于流的任務式的工具
webpack: 是一款模塊化打包工具团赏,webpack是基于配置的箕般,通過配置一些選項來讓webpack執(zhí)行打包任務。
npm i webpack -g
npm i webpack-cli -g (4.0+)
npm i yarn -g
webpack在打包的時候舔清,依靠依賴關系圖丝里,在打包的時候需要告知webpack兩個概念:入口和出口
一般情況下可柿,我們需要使用webpack.config.js進行配置
entry配置項目打包的入口,值可以為單個的字符串執(zhí)行某一個文件的地址丙者,這個時候該文件就是入口文件复斥,webpack會根據(jù)入口文件里各模塊間的關系形成依賴關系圖,然后根據(jù)依賴關系圖進行打包
entry:'./src/app.js',
output:{
? ? path:path.join(__dirname,'build'),
? ? filename:'app.js'
}
但是有的時候我們需要的是多入口械媒,我們就寫成數(shù)組的形式目锭,數(shù)組里的每一個字符串地址指向的都是一個獨立的入口,webpack會將這些入口的依賴打包
entry:['./src/app.js','./src/vendor.js'],
output:{
? ? path:path.join(__dirname,'build'),
? ? filename:'[name].js'//不確定名字的時候纷捞,這里會打包成main.js
}
剛才的兩種entry配置都只會打包出一個js文件痢虹,但是在某一個應用中我們可能需要將js根據(jù)依賴關系打包成多個js文件,并且在多頁面應用中主儡,我們也確實不可能只使用一個js文件奖唯,那么我們就可以使用如下的配置:
? ? entry:{
? ? ? ? app:'./src/app.js',
? ? ? ? vendor:'./src/vendor.js'
? ? },
? ? output:{
? ? ? ? path:path.join(__dirname,'build'),
? ? ? ? filename:'[name]_[hash].js'
? ? }
這樣,因為filename里寫成名字是[name],所以會根據(jù)entry的配置的鍵名來為打包出的js文件命名糜值,hash是每次打包的一個隨機的hash值丰捷,可以用來做版本控制
在這里我們配置打包輸出的一些選項
filename可以確定打包出來的文件的名字,在里面我們可以使用[name],[hash]這樣的占位符
path配置打包出去的文件的路徑寂汇,需要是絕對路徑
在命令行或者終端中執(zhí)行 webpack --env hello命令病往,就相當于在打包的時候傳入一個參數(shù)為hello
在webpack.config.js中可以暴露出一個函數(shù),這個函數(shù)就可以接收到env參數(shù)骄瓣,當然函數(shù)就可以根據(jù)env參數(shù)來有選擇的返回某一個或多個配置對象
module.exports = (env)=>{
? ? if(env=='production'){
? ? ? ? return productionConfig
? ? }
? ? return developmentConfig
}
--watch 可以讓webpack去監(jiān)聽文件的改變停巷。 可以在package.json里的scripts中配置一些快捷操作,通過npm run來運行
在webpack編譯用的是loader榕栏,但是有一些loader無法完成的任務畔勤,交由插件(plugin)來完成,插件的時候需要在配置項中配置plugins選項扒磁,值是數(shù)組庆揪,可以放入多個插件的使用,而一般的插件都是一個構(gòu)造器渗磅,我們只需在plugins數(shù)組中放入該插件的實例即可嚷硫,在實例化插件的時候又可以傳入options,對插件的使用進行配置
html-webpack-plugin
這個插件可以選擇是否依據(jù)模板來生成一個打包好的html文件始鱼,在里面可以配置、title脆贵、template医清、filename、minify等選項卖氨,詳情請查閱文檔
在這個插件里会烙,我們可以使用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相輔相成的有一個server功能工具可以提供開發(fā)的熱更新服務器
npm install webpack-dev-server -g npm install webpack-dev-server -D
第一種啟動方式: 直接執(zhí)行webpack-dev-server,如果有需要配置的選項纸厉,在后面跟上參數(shù)即可。例如
webpack-dev-server --hot true
第二種啟動方式:在webpack.config.js中配置devServer的選項五嫂,執(zhí)行webpack-dev-server就ok
devServer:{
? ? port:9000,
? ? contentBase:'./build',
? ? historyApiFallback: true,
? ? open: true,
? ? proxy:{
? ? }
}
在webpack中專門有一些東西用來編譯文件颗品、處理文件,這些東西就叫l(wèi)oader沃缘,loader的使用就是在配置項中躯枢,設置modules,在modules中設置rule值為數(shù)組槐臀,在數(shù)組里放入多個匹配規(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標簽引入的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可以將引入到js中的css代碼給抽離出來,style-loader可以將抽離出來的css代碼放入到style標簽中
處理sass
{ test:/.scss$/, use:['style-loader','css-loader','sass-loader'] },
將引入項目的css文件晰洒、scss文件抽成一個文件掀亩,引入到頁面中
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
})
因為ExtractTextWebpackPlugin對webpack4支持的不是很好,所以我們這樣解決:
cnpm i extract-text-webpack-plugin@next -D yarn add extract-text-webpack-plugin@next -D
@next下載的就是最最新的版本欢顷,可能是開發(fā)版本
webpack-dev-server進行了一個優(yōu)化槽棍,在跑起服務的時候,會將編譯結(jié)果保存在內(nèi)存里抬驴,不會實時的輸出的打包結(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'] } }
1.創(chuàng)建組件:
使用class來創(chuàng)建組件
class App extends React.Component {
}
2.默認狀態(tài)的設置
在es6中不再使用getInitialState來設置默認狀態(tài),而是在constructor里面直接給this.state上掛載狀態(tài)
class App extends Component { constructor(props){ super(props)
this.state={
doing:'吃飯'
}
}
}
默認屬性的設置
在es6中布持,通過給類設置defaultProps屬性來設置默認屬性
App.defaultProps = { name:'App根組件' }
做屬性傳參驗證
import PropTypes from 'prop-types';
App.propTypes = { name:PropTypes.string }
5.鉤子函數(shù)有變化
getDefaultProps豌拙、getInitialState沒有了
多出了constructor,而這個函數(shù)本身是類的構(gòu)造器,在這里相當于getDefaultProps题暖、getInitialState的結(jié)合
npm install creat-react-app -g
create-react-app my-app //生成一個react開發(fā)模板在my-app目錄 //生成的過程特別緩慢按傅,可以使用yarn工具來下載,也就是說先去下載安裝yarn :npm install yarn -g
當我們要進行二次配置的時候胧卤,需要找到node_modules文件夾里的react-scripts進行配置唯绍,但是當我們執(zhí)行npm run eject就可以將配置文件抽出,方便開發(fā)配置
當我們使用某些組件的時候枝誊,發(fā)現(xiàn)况芒,該組件不需要擁有自己的狀態(tài),只需要接收到外界傳入的屬性之后做出相應的反應即可
這樣的話叶撒,我們可以利用純函數(shù)的方式將其制作成無狀態(tài)組件绝骚,提高性能
import React from 'react'
const Button = (props)=>{
return <button onClick={props.handler}>我要花錢</button>
}
export default Button
在2014年耐版,facebook提出了Flux,F(xiàn)lux 是一種架構(gòu)思想压汪,專門解決軟件的結(jié)構(gòu)問題粪牲。它跟MVC 架構(gòu)是同一類東西,但是更加簡單和清晰止剖。
其實FLUX在react里的應用就類似于vue中的vuex的作用腺阳,但是
在vue中,vue是完整的mvvm框架滴须,而vuex只是一個全局的插件
react只是一個視圖層的框架舌狗,在flux是一個架構(gòu)思想,我們在做項目的時候使用flux架構(gòu)的話要比單純使用react要簡單很多扔水,這個時候痛侍,react在整個FLUX架構(gòu)中擔任某一個角色的
react在這里只是充當了FLUX架構(gòu)體系中的view層
Flux的組成部分:
View: 視圖層
ActionCreator(動作創(chuàng)造者):視圖層發(fā)出的消息(比如mouseClick)
Dispatcher(派發(fā)器):用來接收Actions、執(zhí)行回調(diào)函數(shù)
Store(數(shù)據(jù)層):用來存放應用的狀態(tài)魔市,一旦發(fā)生變動主届,就提醒Views要更新頁面
Flux的流程:
組件獲取到store中保存的數(shù)據(jù)掛載在自己的狀態(tài)上
用戶產(chǎn)生了操作,調(diào)用actions的方法
actions接收到了用戶的操作待德,進行一系列的邏輯代碼君丁、異步操作
然后actions會創(chuàng)建出對應的action,action帶有標識性的屬性
actions調(diào)用dispatcher的dispatch方法將action傳遞給dispatcher
dispatcher接收到action并根據(jù)標識信息判斷之后将宪,調(diào)用store的更改數(shù)據(jù)的方法
store的方法被調(diào)用后绘闷,更改狀態(tài),并觸發(fā)自己的某一個事件
store更改狀態(tài)后事件被觸發(fā)较坛,該事件的處理程序會通知view去獲取最新的數(shù)據(jù)
React 只是 DOM 的一個抽象層印蔗,并不是 Web 應用的完整解決方案。有兩個方面丑勤,它沒涉及华嘹。
代碼結(jié)構(gòu)
組件之間的通信
2014年 Facebook 提出了 Flux 架構(gòu)的概念,引發(fā)了很多的實現(xiàn)法竞。2015年耙厚,Redux 出現(xiàn),將 Flux 與函數(shù)式編程結(jié)合一起岔霸,很短時間內(nèi)就成為了最熱門的前端架構(gòu)薛躬。
如果你不知道是否需要 Redux,那就是不需要它
只有遇到 React 實在解決不了的問題秉剑,你才需要 Redux
簡單說泛豪,如果你的UI層非常簡單,沒有很多互動侦鹏,Redux 就是不必要的诡曙,用了反而增加復雜性。
用戶的使用方式非常簡單
用戶之間沒有協(xié)作
不需要與服務器大量交互略水,也沒有使用 WebSocket
視圖層(View)只從單一來源獲取數(shù)據(jù)
需要使用redux的項目:
用戶的使用方式復雜
不同身份的用戶有不同的使用方式(比如普通用戶和管理員)
多個用戶之間可以協(xié)作
與服務器大量交互价卤,或者使用了WebSocket
View要從多個來源獲取數(shù)據(jù)
從組件層面考慮,什么樣子的需要redux:
某個組件的狀態(tài)渊涝,需要共享
某個狀態(tài)需要在任何地方都可以拿到
一個組件需要改變?nèi)譅顟B(tài)
一個組件需要改變另一個組件的狀態(tài)
redux的設計思想:
Web 應用是一個狀態(tài)機慎璧,視圖與狀態(tài)是一一對應的。
所有的狀態(tài)跨释,保存在一個對象里面(唯一數(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)建了帶有標示性信息的action 5.actions將action通過調(diào)用store.dispatch方法發(fā)送到了reducer中 6.reducer接收到action并根據(jù)標識信息判斷之后返回了新的state 7.store的state被reducer更改為新state的時候岁疼,store.subscribe方法里的回調(diào)函數(shù)會執(zhí)行,此時就可以通知view去重新獲取state
注意:flux缆娃、redux都不是必須和react搭配使用的捷绒,因為flux和redux是完整的架構(gòu),在學習react的時候贯要,只是將react的組件作為redux中的視圖層去使用了暖侨。
reducer必須是一個純函數(shù):
Reducer 函數(shù)最重要的特征是,它是一個純函數(shù)崇渗。也就是說字逗,只要是同樣的輸入,必定得到同樣的輸出宅广。
純函數(shù)是函數(shù)式編程的概念葫掉,必須遵守以下一些約束。
不得改寫參數(shù)
不能調(diào)用系統(tǒng) I/O 的API
不能調(diào)用Date.now()或者Math.random()等不純的方法乘碑,因為每次會得到不一樣的結(jié)果
由于 Reducer 是純函數(shù)挖息,就可以保證同樣的State,必定得到同樣的 View兽肤。但也正因為這一點套腹,Reducer 函數(shù)里面不能改變 State,必須返回一個全新的對象资铡,請參考下面的寫法电禀。
// State 是一個對象
function reducer(state, action) {
? return Object.assign({}, state, { thingToChange });
? // 或者
? return { ...state, ...newState };
}
// State 是一個數(shù)組
function reducer(state, action) {
? return [...state, newItem];
}
最好把 State 對象設成只讀。你沒法改變它笤休,要得到新的 State尖飞,唯一辦法就是生成一個新對象。這樣的好處是,任何時候政基,與某個 View 對應的 State 總是一個不變的對象贞铣。
我們可以通過在createStore中傳入第二個參數(shù)來設置默認的state,但是這種形式只適合于只有一個reducer的時候
劃分reducer
因為一個應用中只能有一個大的state沮明,這樣的話reducer中的代碼將會特別特別的多辕坝,那么就可以使用combineReducers方法將已經(jīng)分開的reducer合并到一起
注意:
分離reducer的時候,每一個reducer維護的狀態(tài)都應該不同
通過store.getState獲取到的數(shù)據(jù)也是會安裝reducers去劃分的
劃分多個reducer的時候荐健,默認狀態(tài)只能創(chuàng)建在reducer中酱畅,因為劃分reducer的目的,就是為了讓每一個reducer都去獨立管理一部分狀態(tài)
市場上的react-router的版本有1江场、2纺酸、3、4,1-3的差別不大址否,使用于16.0.0以下的版本
react-router 4.0 適用于16.0.0以上
在這里使用15.6.1的react餐蔬。這個版本的react允許使用React.createClass來創(chuàng)建組件,在16以上只能使用class類的方式來創(chuàng)建
渲染根組件的時候在张,最外層包裹上Router組件用含,在其上可以設置history屬性,值可以是hashHistory||browserHistory
當值為hashHistory的時候帮匾,url的變化為hash值的變化啄骇,router會去檢測hash變化來實現(xiàn)組件的切換
當值為browserHistory的時候,url的變化為path的變化瘟斜,需要后端進行配置
Router中使用Route組件來描述每一級路由缸夹,Route上有path、component屬性螺句,代表著當path改變成...的時候虽惭,就渲染..組件
在需要切換路由組件的地方,通過this.props.children來表示對應路由組件
在Route中可以多次嵌套Route來實現(xiàn)多級路由
IndexRoute可以設置該路由中的默認子路由
IndexRedirect可以設置在進入該路由之后馬上跳轉(zhuǎn)到哪里
使用Redirect組件可以做到從一個路由馬上重定向到其他路由,利用這樣的屬性蛇尚,當我們form設置為'*'的時候芽唇,就可以將匹配不到的路由重定向到某げ路由下
可以在配置Route的時候給path里加入/:param 才表示此路由需要參數(shù)
傳入的時候,querystring參數(shù)可以在Link里的query中傳入和設置取劫,在目標組件中匆笤,通過this.props中的,params谱邪、routePrams炮捧、location等來接收參數(shù)
可以通過過Router傳入routes參數(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'))
編程式導航
在路由組件中通過this.props.history獲取到history對象咆课,利用里面push末誓、replace、go书蚪、goBack方法來進行隱式跳轉(zhuǎn)
可以從react-router中引入browserHistory或者hashHistory調(diào)用里面的push喇澡、replace、go善炫、goBack方法來進行隱式跳轉(zhuǎn)
可以通過在路由配置上設置 onLeave和onEnter路由鉤子來監(jiān)聽路由的變化
關于React的UI組件庫市場上也有很多撩幽,在這里我們使用螞蟻金服開發(fā)的AntDesign組件庫
這是PC端的库继,移動端的是Antd-Mobile
這個庫或者說工具是redux的開發(fā)者專門為react創(chuàng)建出來的箩艺,為我們在react中使用redux提供便利
起到的是橋梁的作用,能將react和redux更好的連接在一起
React-Redux 將所有組件分成兩大類:UI 組件/木偶組件(presentational component)和容器組件/智能組件(container component)宪萄。
UI 組件有以下幾個特征艺谆。
只負責 UI 的呈現(xiàn),不帶有任何業(yè)務邏輯
沒有狀態(tài)(即不使用this.state這個變量)
所有數(shù)據(jù)都由參數(shù)(this.props)提供
不使用任何 Redux 的 API
容器組件的特征恰恰相反拜英。
負責管理數(shù)據(jù)和業(yè)務邏輯静汤,不負責 UI 的呈現(xiàn)
帶有內(nèi)部狀態(tài)
使用 Redux 的 API
只要記住一句話就可以了:UI 組件負責 UI 的呈現(xiàn),容器組件負責管理數(shù)據(jù)和邏輯居凶。
你可能會問虫给,如果一個組件既有 UI 又有業(yè)務邏輯,那怎么辦侠碧?回答是抹估,將它拆分成下面的結(jié)構(gòu):外面是一個容器組件,里面包了一個UI 組件弄兜。前者負責與外部的通信药蜻,將數(shù)據(jù)傳給后者,由后者渲染出視圖替饿。
React-Redux 規(guī)定语泽,所有的 UI 組件都由用戶提供,容器組件則是由 React-Redux 自動生成视卢。也就是說踱卵,用戶負責視覺層,狀態(tài)管理則是全部交給它据过。
使用方法及步驟:
使用Provider組件惋砂,包裹在應用的最外層饱溢,并為Provider注入store屬性补疑,此時,Provider就會將自己的store屬性傳遞給子組件組合中的容器組件
使用connect函數(shù)犀变,可以根據(jù)一個現(xiàn)有的UI組件生成一個容器組件榨呆,且我們在使用的時候罗标,其實一直在使用的都是容器組件庸队,connect函數(shù)執(zhí)行之后返回一個函數(shù),將返回的函數(shù)傳入UI組件并執(zhí)行之后就會生成一個容器組件
connect函數(shù)有兩個參數(shù):mapStateToProps,mapDispatchToProps
mapStateToProps的作用很簡單闯割,就是將redux中的state傳遞到UI組件的props上彻消,此參數(shù)是一個函數(shù),接收到store的state然后再返回一個對象宙拉,返回的對象中的屬性就會傳遞到UI組件的屬性上
mapStateToProps對store進行了訂閱宾尚,只要state更改,mapStateToProps會自動執(zhí)行并獲取到最新的state傳入到UI組件的屬性上
mapDispatchToprops 函數(shù)谢澈,接收到dispatch參數(shù)煌贴,其實就是store.dispatch,返回的對象中設置的方法可以使用到dispatch锥忿,且能傳入到UI組件的屬性上
那么牛郑,有了mapDistpatchToProps之后,我們就不需要actions了嗎敬鬓?
我們需要將一些復雜的業(yè)務邏輯淹朋,或者說異步的業(yè)務邏輯抽離出來放入到actions里面去,也就是后所mapDispatchToProps里自己創(chuàng)建的只是一些簡單的方法就可以了
第一次使用react-redux等工具的做法
創(chuàng)建了actionCreator钉答,專門生成action础芍,又設置 了actions,在actions里放一些異步的数尿、復雜的操作之后仑性,調(diào)用actionCreator生成action再dispatch到reducer
其實我們上面創(chuàng)建actions的目的,就是因為ActionCreator不能做復雜的動作砌创,其實我們可以使用redux-thunk來對reducer創(chuàng)建中間件虏缸,讓actionCreator的方法能返回一個函數(shù),這個函數(shù)就可以接收到dispatch嫩实,且做出異步操作之后dispatch出action刽辙,也就是說,我們不需要再創(chuàng)建actions來分離異步復雜操作甲献,而且直接可以在ActionCreator里寫異步方法
步驟:
對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}
其實宰缤,現(xiàn)在有這樣的流行做法:
將所有的數(shù)據(jù)都交由redux管理,這樣的話晃洒,我們的組件在UI層的邏輯就更純粹了,而且可以做到數(shù)據(jù)緩存慨灭,比如,A組件獲取了數(shù)據(jù)放入到redux中球及,當A組件被切換掉之后重新切換回來的時候氧骤,數(shù)據(jù)依然在redux中可以找到,也就是說直接取出來用就ok吃引,不需要重新獲取
ref推薦使用函數(shù)的方法:
字符串方式
<Son ref="son"></Son>
//
this.refs.son
函數(shù)方式(推薦)
<Son ref={(el)=>{this.son = el}}></Son>
//
this.son
react的特點不包括什么筹陵?
聲明式設計刽锤、高效、靈活朦佩、(雙向數(shù)據(jù)流)
動畫可以使用哪個第三方插件實現(xiàn):
(ReactTransitionGroup)/animate.css/transitionTranslate/redux-thunk
ReactRouter中并思,路由的onLeave應該寫在哪里:
路由對應的組件中、父組件中语稠、(路由組件中)宋彼、最外層大組件中
react-redux中的connect方法的返回值是一個: ContainerComponent = connect()(UIComponent) 對象、(容器組件)仙畦、UI組件输涕、數(shù)組
react中常提到的中間件的概念,指的是:
react中間件议泵、flux中間件占贫、(redux中間件)、react-redux中間件
redux設計的三大原則:
(store唯一/唯一數(shù)據(jù)源先口、 state只讀、 reducer是純函數(shù)) reducer只讀
哪些不是react-router的組件:
(Provider)瞳收、Route碉京、Router、(MapStateToProps)
下面哪些方法可以使componentWillUpdate執(zhí)行
屬性更改或者狀態(tài)更改
請簡述對虛擬dom的理解螟深,為什么使用虛擬DOM可以極大的提升react的性能
虛擬dom是真實dom的js對象映射谐宙,使用虛擬dom,避免對原生dom的創(chuàng)建和比對界弧,取而代之的創(chuàng)建和比對的是js對象
原生dom的創(chuàng)建和比對是非常消耗性能的凡蜻,而js對象的對比和創(chuàng)建對性能開銷很小,從這種方式來提供應用的性能
請說明在react中ref的作用垢箕,并寫出使用ref的兩種方式划栓,說明哪一種是官方推薦的
ref可以使我們在react對dom或者子組件做出標記并獲取:
//this.refs.son <Son ref={(el)=>{this.son = el}}>//this.son(官方推薦)
說明react中条获,父子組件項目傳值的方式忠荞,并說明在大型項目中為什么要引入flux或者redux這種開發(fā)架構(gòu) 父組件將自己的狀態(tài)當成屬性傳遞給子組件 父組件將自己的方法傳遞給子組件,子組件在調(diào)用的時候傳參 父組件通過ref獲取到子組件帅掘,調(diào)用子組件的方法傳參
react是一款視圖層的輕量級前端框架委煤,大量的非父子組件通信、狀態(tài)共享會導致整個項目數(shù)據(jù)復雜修档,難以維護碧绞,所以react不適合處理大量的數(shù)據(jù)通信,為了解決這個問題吱窝,引入了FLUX讥邻、REDUX這樣的數(shù)據(jù)架構(gòu)寓免,react結(jié)合FLUX或者redux才能實現(xiàn)比較復雜的前端項目
在react中,列表循環(huán)盡量不要使用index作為key值计维,這和diff算法有關系袜香,請簡述diff算法中key值有什么作用,為什么key中使用index值會降低代碼性能
key值是diff算法中對比兩個虛擬dom的最重要參考鲫惶,決定了哪些列表中的組件可以復用蜈首,如果使用index作為key中,列表數(shù)據(jù)改變后欠母,會導致同一個dom元素的key中發(fā)送改變欢策,本來可以復用的組件必須重新創(chuàng)建,降低頁面性能赏淌,除非列表不需要改變踩寇,一般情況不使用index作為key值
請列舉你所了解的react中的性能優(yōu)化
沒必要存在store中的數(shù)據(jù),存在state中就可以
函數(shù)的this執(zhí)行放在constructor中改變
和頁面顯示無關的數(shù)據(jù)不要放在state中
shouldComponentUpdate來判斷組件是否需要重新render(使用pureComponent)
使用性能分析工具進行性能分析六水,找問題解決問題
13.1 pureComponnet pureComponnet里如果接收到的新屬性或者是更改后的狀態(tài)和原屬性俺孙、原狀態(tài)相同想等的話,就不會去重新render了 在里面也可以使用shouldComponentUpdate掷贾,而且睛榄。是否重新渲染以shouldComponentUpdate的返回值為最終的決定因素 class ABC extends pureComponnet {
}
請說明react中引入redux-thunk、redux-promise這兩種中間件可以解決什么樣的問題
通常情況下想帅,action只是一個對象场靴,不能包含異步操作,這導致了很多創(chuàng)建action的邏輯只能寫在組件中港准,代碼量較多也不便于復用旨剥,同時對該部分代碼測試的時候也比較困難,組件的業(yè)務邏輯也不清晰浅缸,使用中間件了之后轨帜,可以通過actionCreator異步編寫action,這樣代碼就會拆分到actionCreator中疗杉,可維護性大大提高阵谚,可以方便于測試、復用烟具,同時actionCreator還集成了異步操作中不同的action派發(fā)機制梢什,減少編碼過程中的代碼量
畫圖說明redux的架構(gòu),寫出redux中常用的函數(shù)
dispatch , subscribe,combineReducers,getState,createStore
簡述react-redux結(jié)合react-router在項目中的使用方式
創(chuàng)建store朝聋,編寫reducer
在外面嵌套嗡午,將store傳遞給各個子組件
編寫UI組件
使用react-redux的connect方法結(jié)合mapStateToProps、mapDispatchToProps生成容器組件冀痕,容器組件和store連接在一起
router4
創(chuàng)建store荔睹,編寫reducer
在外面嵌套狸演,將store傳遞給各個子組件
編寫UI組件
使用react-redux的connect方法結(jié)合mapStateToProps、mapDispatchToProps生成容器組件僻他,容器組件和store連接在一起
如果某一個組件不是路由組件宵距,卻需要使用router相關api,并且還需要使用store中的state的時候吨拗,需要在最外層包裹withRouter满哪,里面再使用connect生成容器組件
下列說法錯誤的是:
(React是一款專注于數(shù)據(jù)層的前端框架)、react中需要調(diào)用setState方法來重置狀態(tài)劝篷、react中的虛擬dom可以提升框架自身的性能哨鸭、(react是一款符合MVVM設計思想的前端框架)
關于前端組件化說法錯誤的是:
前端組件使得復雜的代碼得以被更好的劃分、組件化的代碼設計方式增加了代碼的可復用性娇妓、(在拆分組件的時候?qū)⒔M件拆分的越小越細越好)像鸡、組件化開發(fā)是一種設計思想,不是react獨享的
在react中哈恰,異步獲取ajax數(shù)據(jù)一般放在那個生命周期函數(shù):componentWillMount
使用es6定義組件只估,可以在那個生命周期鉤子函數(shù)里使用this.state=state 對state進行賦值。而不需要調(diào)用this.setState方法:constructor
在redux中蕊蝗,重要的組成部分不包括:
store仅乓、action、reducer蓬戚、(dispatcher)
webpack中html-webpack-plugin插件可以完成的任務是:
(在打包輸出目錄中自動生成index.html)、(向打包目錄中的index.html文件內(nèi)插入打包生成的js文件引用)宾抓、將js源碼中引用的css代碼抽離ちゅ單獨的css文件并放置到打包輸出目錄子漩、(像打包輸出目錄中的index.html文件插入打包生成的css引用)
關于jsx,說明正確的是:(ad)
a:jsx中可以通過{}來使用js的表達式石洗,b:jsx中可以通過{}來使用js的代碼幢泼,c:jsx中可以使用style={color:'red'}來設置元素的樣式、d:jsx代碼會被解析成一個js對象
react組件被掛載到頁面 的時候讲衫,被執(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中和頁面無關的屬性發(fā)送變化時招驴,render不會執(zhí)行,d:shouldComponentUpdate函數(shù)的兩個參數(shù)分別是當前的state和當前的props
在react組件中枷畏,當(props或者state)發(fā)送變化的時候别厘,會導致render生命周期函數(shù)重新執(zhí)行
使用react-redux時,頁面上的組件需要被拆分成(容器)組件和(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中元素的真實dom節(jié)點或子組件)
es5語法創(chuàng)建react組件比es6多了兩個生命周期函數(shù)(getDefaultProps/getInitialState)
請簡述react-router中hashHistory和browserHistory的區(qū)別:
這是react-router中設置監(jiān)聽url路徑變化的兩種方式,hashHistory在切換路由的時候會在url上跟著哈希值冗懦,browserHistory通過判斷path變化才切換路由爽冕,且path變化的時候后端可以接收到請求,需要后端配置忽略掉
請畫圖說明flux中的單向數(shù)據(jù)流
用戶訪問View-》view發(fā)出用戶的action-》dispatcher收到action要求store進行更新-》store更改后披蕉,觸發(fā)一個事件-》view接收到該事件的觸發(fā)颈畸,更新頁面
簡述react-redux的用法
創(chuàng)建store、reducer嚣艇,通過provider將store傳遞給各個子組件承冰,創(chuàng)建ui組件,生成容器組件食零,利用connect將store和容器組件連接
說明redux設計和使用的三大原則:
唯一數(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)=>{
? ? ? ? ...
? ? }
}
}