一、前言
其實在前面介紹組件的時候,已經用到了這些知識,本章節我們重點分析下,並與Vue的用法比較。
二、事件
Reactjs中事件處理方式與原生態DOM的非常類似,只不過使用JSX的語法,採用的是小駝峯的表達方式,比如onClick,onChange,我們以SearchHot.js的"下一批"功能爲例,當點擊按鈕,更新熱詞。
return (
<div className="d">
<div className="s">大家都在搜:<a href="#" onClick={this.changeHot} style={{marginLeft:100}}>下一批</a></div>
<ul className="su">
{/*引用變量表達式 */}
{hotList}
</ul>
</div>
)
在a標籤上增加onClick響應事件,調用的方法爲changeHot,下面我們對該方法定義
changeHot(){
this.setState({hotItem:['電視','手機','電腦','平板']})
}
在此方法中,我們調用setState更新hotItem屬性。如果直接運行,將會報this爲undefined的錯誤,我們還需要在組件初始化方法中,將該方法綁定到組件對象。
constructor(props){
// 1、調用父類構造方法
super(props);
//2、初始化state對象
this.state = {hotItem:['口罩','手套','酒精',]};
//綁定this
this.changeHot = this.changeHot.bind(this);
}
除了這種bind方式,還可以將changeHot改造爲箭頭函數,在箭頭函數中,this指向的函數的本身,而不是函數的調用者,可以參考ES6系列教程第八篇--箭頭函數詳解
changeHot = () =>this.setState({hotItem:['電視','手機','電腦','平板']});
我們在a標籤上增加一個默認事件,href="search.json"
return (
<div className="d">
<div className="s">大家都在搜:<a href="search.json" onClick={this.changeHot} style={{marginLeft:100}}>下一批</a></div>
<ul className="su">
{/*引用變量表達式 */}
{hotList}
</ul>
</div>
)
此時再次點擊按鈕,發現頁面跳轉到search.json,這是因爲執行了默認事件。現在我們需要阻止默認事件或者冒泡。
changeHot(e){
e.preventDefault();
e.stopPropagation();
this.setState({hotItem:['電視','手機','電腦','平板'] })
}
再次點擊時,功能正常。
事件方法中會隱形傳入一個event參數,通過調用該參數的preventDefault(),stopPropagation()阻止默認事件以及冒泡。
傳參是事件的基本的功能,我們看下React如何實現傳參。
return (
<div className="d">
<div className="s">大家都在搜:<a href="search.json" onClick={this.changeHot.bind(this,1,"ok")} style={{marginLeft:100}}>下一批</a></div>
<ul className="su">
{/*引用變量表達式 */}
{hotList}
</ul>
</div>
)
在調用的時候綁定this對象,並依次傳遞相關的參數(注意:事件方法已經綁定了this,可以不需要再在初始化方法中重複綁定),在changeHot中接受並處理參數。
changeHot(id,name,e){
e.preventDefault();
e.stopPropagation();
this.setState({hotItem:['電視','手機','電腦','平板'] });
console.log("id:"+id);
console.log("name:"+name)
}
無論傳遞多少參數,隱形的event參數始終處於最後的位置。
在Vue中,事件的處理使用的是v-on內置的指令,並通過事件的修飾符簡化了諸如阻止默認事件,冒泡等寫法。而JSX語法中沒有指令的概念,其處理方式更接近原生的DOM。
三、條件
在前面章節中,我們介紹JSX僅支持三目以及&&符運算的嵌入,而不支持if...else條件語句。我們先看三目運算和&&。
在SearchBox.js中,我們用this.state.isSearch標誌符控制button按鈕的disabled屬性,當this.state.isSearchShow爲true,按鈕有效,false則無效。
return(
<div style={{width:300,margin:10}}>
<input type="text" style={{width:150}} onChange={this.handleChange}></input>
<button onClick={this.onSearchClick} disabled={this.state.isSearchShow?"":"disabled"}>搜索</button>
</div>
)
也可以改寫&&運算
return(
<div style={{width:300,margin:10}}>
<input type="text" style={{width:150}} onChange={this.handleChange}></input>
<button onClick={this.onSearchClick} disabled={!this.state.isSearchShow&&"disabled"}>搜索</button>
</div>
)
在js中,true && expression
總是會返回 expression
, 而 false && expression
總是會返回 false。
如果是多個條件組合,三目運算就無法滿足了,比如不同的狀態,顯示button的不同背景色,此時就需要用到if...else。
render(){
//定義color變量,並根據狀態顯示不同的顏色
let color;
if(this.state.color<10){
color="green"
}else if(this.state.color<20){
color="blue"
}else if(this.state.color<30){
color="red"
}else{
color="orange"
}
return(
<div style={{width:300,margin:10}}>
<input type="text" style={{width:150}} onChange={this.handleChange}></input>
<button onClick={this.onSearchClick} disabled={!this.state.isSearchShow&&"disabled"} style={{backgroundColor:color}}>搜索</button>
</div>
)
}
雖然JSX中無法直接使用if...else,但可以通過在代碼段中,定義color的變量並條件賦值,再在JSX中插入color變量表達式。
在Vue中使用v-if,v-else指令進行條件控制,並對控件的隱藏和顯示提供了v-show,使用更方便。
四、列表
Reactjs中對於列表使用map方法進行遍歷,並拼裝成JSX表達式。以SearchList.js的搜索結果列表展示爲例:
render(){
//1、從props中獲取searchListVal,封裝列表
const searchValList = this.state.searchListVal.map((value)=>
(<li>
{value}
</li>)
);
return(
<div className="d">
<div className="s">搜索結果:</div>
{/* 引入插槽代碼 */}
{this.props.children}
<ul className="u" ref={this.searchul}>
{/* 引用對象表達式*/}
{searchValList}
</ul>
</div>
)
}
遍歷searchListVal數組,拼裝li表達式。此時我們打開控制檯,會發現有如下的warning
這個告警的意思,list應增加一個key屬性。
const searchValList = this.state.searchListVal.map((value)=>
(<li key={value.toString()}>
{value}
</li>)
);
告警消失,那加上這個key有什麼作用呢?
這要從diff算法的原理說起。diff運算時,對於同一節點下的子元素,進行遍歷比較,如果不一致,則產出一個mutation。如下:
//老節點
<ul>
<li>first</li>
<li>second</li>
</ul>
//新節點
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
該樣例中,ul的子元素li,新老節點中第一個和第二個是完全一樣的,所以無需變動,僅在尾部插入第三個li元素,就可以完成節點的更新,效率很高。如果是頭部插入一個li呢?
/老節點
<ul>
<li>first</li>
<li>second</li>
</ul>
//新節點
<ul>
<li>third</li>
<li>first</li>
<li>second</li>
</ul>
在diff運算過程中,比較發現第一個,第二個li元素不一樣(老節點的第一個爲first,新節點的第一個爲third),將會銷燬重建,效率會非常低。效率最高的的更新操作,僅在頭部插入一個third,其他節點保持不變。爲了解決這個問題,引入了key。
/老節點
<ul>
<li key="1">first</li>
<li key="2">second</li>
</ul>
//新節點
<ul>
<li key="3">third</li>
<li key="1">first</li>
<li key="2">second</li>
</ul>
diff運算過程中,不是直接遍歷比較,而是優先查找是否有相同的key,新老節點都有key爲1,2的li元素,且值沒有變化,這兩個節點將保持不變,最終僅在頭部插入key爲3的li元素,實現了高效率的更新。
所以,key需要有以下兩個特點:
- key一定是在同級的兄弟節點間設置,因爲非同級節點不會進行diff運算。
- key的值在兄弟節點間必須唯一,一般用id等,最好不要用列表索引(索引可能會變化)
在Vue用使用v-for進行列表渲染,其中也需要設置key,原理是一致的。
五、表單
表單標籤包括select,input,textarea,checkbox,radio等,他們是最能說明雙向綁定的過程,比如SearchBox.js中的搜索輸入框input
handleChange(e){
this.setState({searchVal:e.target.value});
}
....
return(
<div style={{width:300,margin:10}}>
<input type="text" style={{width:150}} onChange={this.handleChange} value={this.state.searchVal}></input>
<button onClick={this.onSearchClick} >搜索</button>
</div>
)
input框的每次輸入,通過onChange捕捉變化事件,並響應handleChange方法,將input的當前值更新到state中的searchVal(這就是view->model過程);searchVal賦值給input的value,searchVal值的變化,又反饋到input的value(這就是model->view)。有同學會問,這這麼做不是多此一舉麼,其實不然,在實際使用過程中,我們會在handleChange中對輸入值e.target.value進行處理,比如說銀行卡號,4個數字一組,做分割處理,再設置給input框輸出展示。
Reactjs還提供一種操作DOM獲取input的輸入值的方式,即使用ref。
首先在構造函數中定義ref
constructor(props){
super(props);
this.state = {searchVal:"",isSearchShow:true,color:25};
this.onSearchClick = this.onSearchClick.bind(this);
this.handleChange = this.handleChange.bind(this);
//定義ref
this.input = React.createRef();
}
然後設置input的ref屬性
return(
<div style={{width:300,margin:10}}>
<input type="text" style={{width:150}} onChange={this.handleChange} ref={this.input} value={this.state.searchVal}></input>
<button onClick={this.onSearchClick} >搜索</button>
</div>
)
就可以使用ref獲取input的對象句柄,比如handleChange通過ref的方式獲取input的值
handleChange(e){
this.setState({searchVal:this.input.current.value});
}
使用的時候別忘了current關鍵字。此時input可以通過ref進行DOM操作,稱爲非受控組件。雖然這種模式打破了數據驅動view的原則,但是有時候還是比較方便的,比如獲取滾動條的高度。
在Vue中可有類似的做法,通過自定義的指令操控DOM節點。
六、總結
本章節介紹了Reactjs的事件,條件,列表,表單的基本用法,並與vue進行了簡單的比較。
事件的隱形event參數的使用,阻止默認事件以及冒泡,傳遞參數。
條件的三目運算,&&符,以及if...else條件的使用
列表使用map遍歷,以及key的原理。
表單的兩種不同的獲取對象值的方法。