這是React分類下的第一篇文章,是在瞭解了一些基本面後,看 "Tyler" 文章,邊看邊理解邊寫的。 React可以看做是MVC中的V,關註的是視圖層。React的組件就像Angular的Directive,包括了HTML,CSS,JS以及相關數據等。React的組件被定義在了以"JSX"為尾碼的 ...
這是React分類下的第一篇文章,是在瞭解了一些基本面後,看Tyler文章,邊看邊理解邊寫的。
React可以看做是MVC中的V,關註的是視圖層。React的組件就像Angular的Directive,包括了HTML,CSS,JS以及相關數據等。React的組件被定義在了以"JSX"為尾碼的文件中,這些JSX文件會被最終編譯成Javascript文件。
來看一個最基本的寫法:
var HelloWorld = React.createClass({
render: function(){
return (
<div>
Hello World!
</div>
)
}
});
ReactDOM.render(<HelloWorld />, document.getElementBy('app'));
以上,我們可以瞭解到:
- 組件創建:
React.createClass
用來創建組件,接收一個object,render欄位必須有 - 組件名稱:就像這裡的HelloWorld,通常是大寫
- 組件渲染:通過
ReactDOM.render
來渲染組件,該方法接收2個參數,一個參數代表組件,另一個代表渲染的位置 - 組件的呈現方式:組件是以類似HTML元素的形式呈現的,另外,如果組件中有變數,變數會以類似HTML元素屬性的形式呈現
- JSX:以上的的
<div>Hello World!</div>
和通常的HTML代碼不一樣,是放在以jsx為尾碼的文件中的,最終會被轉換成一個Javascript對象,最終React會創建一個"virtual DOM",React會通過React.createElement("div", null, "Hello Wolrd")
創建虛擬的DOM。
1、React在"virtual DOM"這部分是如何工作的?
→ 檢查數據發生變化的部分
→ 重新渲染virutal DOM
→ 與上一次的virtual DOM進行比較
→ 在實際DOM上只更新發生變化的部分
2、State
var HelloUser = React.createClass({
getInitialState: function(){
return {
username: 'firstusername'
}
},
render: function(){
return (
<div>
Hello {this.state.username}
</div>
)
}
});
- 設置組件初始狀態:通過
getInitialState
方法返回object對象,為state中的欄位賦值 - 獲取組件狀態:通過
this.state.欄位名
來獲取欄位狀態 - 設置組件狀態:
setState
雖然在上面沒有提及,但這個方法時被用來真正設置組件狀態的。而且這個setState
方法是最常用的,當頁面有數據變化,Reacct會觸發setState
方法,渲染virtual DOM,與上一次的virtual DOM進行比較,最後更新發生變化的部分。
2.1 通過事件改變狀態
以上,是通過getInitialState
方法設置了一個初始狀態,如何通過事件來觸發改變狀態呢?比如有一個<input>
,通過它的onChange
方法來觸發改變狀態。
var HelloUser = React.creatClass({
getInitialState: function(){
return {
username: 'darren'
}
},
handleChange: function(e){
this.setState({
usename: e.target.value
});
},
render: function(){
return (
<div>
Hello {this.state.username}<br/>
Change Name:<input type="text" value={this.state.username} onChange={this.handleChange}>
</div>
)
}
});
- input的onChange事件交給了this.handleChange
- 在
handleChange
方法中,通過setState
改變變數的狀態,接著,React渲染一個新的virtual DOM, 與上一次的virtual DOM比較差異,最後只更新發生變化的那部分DOM - React的
onChange
方法用來觸發事件
2.2 嵌套組件的狀態傳遞
現在,我們知道瞭如何設置組件的初始狀態,也知道如何如何設置組件的狀態,現在來到另外一個話題:當主鍵有嵌套的時候,如何把父組件的狀態傳遞給子組件呢?
先來看沒有組件嵌套的情況:
var HelloUser = React.createClass({
render: function(){
return (
<div> Hello, {this.props.name}</div>
)
}
});
ReactDOM.render(<HelloUser name="darren" />, document.getElementById('app'));
- 組件在jsx語法中,是以類似頁面元素的形式呈現的,比如這裡的
<HelloUser />
,組件中的變數,比如這裡的name欄位,在jsx語法中是以屬性名存在的,比如這裡的<HelloUser name="darren">
- 對ReactDOM來說,它知道
<HelloUser name="darren" />
就是需要渲染的組件,HelloUser就是組件的名稱,給name賦的值最終賦值給了props屬性
再來看存在組件嵌套的情況下,如何傳遞State。
被嵌套的組件:
var ShowList = React.createClass({
render: function(){
var listItems = this.props.names.map(function(friend){
return <li>{{friend}}</li>;
});
return (
<div>
<h3>Friends</h3>
<ul>
{listItems}
</ul>
</div>
)
}
});
以上,ShowList組件的視圖渲染需要外界給names欄位賦上數組。也就是<ShowList names=... />
中names屬性需要賦值。從哪裡賦值呢?可以從嵌套組件的state中獲取。
好,那就來到嵌套組件:
var ParentContainer = React.createClass({
getIntitialState: function(){
return {
name: 'darren',
friends:['a','bob']
}
},
render: function(){
return (
<div>
<h3>Name: {this.state.name}</h3>
<ShowList names={this.state.friends}>
</div>
)
}
});
- 我們知道
getIntitialState
中返回對象的各個欄位是放在了state中的 - state中的friends是數組,賦值給了ShowList的names屬性
- 嵌套組件和被嵌套組件之間state的傳遞看上去像一條河流,嵌套組件是河流的上游,被嵌套組件是下游,河水是單向流動的,上游的state向下游流動,傳遞給了下流的被嵌套組件
現在,組件看起來就像C#中對類的封裝。React的組件是否和C#中的類有更多的相似性呢?
假設,在顯示朋友列表的頁面再增加一塊區域,用來添加朋友信息。那,我們有必要為這一塊添加的區域定義一個組件:
var AddFriend = React.createClas({
getIntialState: function(){
return {
newFriend: ''
}
},
updateNewFriend: function(e){//更新就是為state的newFriend欄位賦上當前的值
this.setState({
newFriend: e.target.value
});
},
handleAddNew: function(){
this.props.addNew(this.state.newFriend);
this.setState({
newFriend: ''
});
},
render: function(){
return (
<div>
<input type="text" value={this.state.newFriend} onChange={this.updateNewFriend} />
<button onClick={this.handleAddNew}>Add Friend</button>
</div>
)
}
});
- input文本框的值取決於state中的newFriend欄位值
- 為input添加了onChange事件,該事件由updateNewFriend這個代表函數的欄位所定義
- 在updateNewFriend函數中,把文本框的當前值賦值給state中的newFriend欄位
- 為button添加了一個onClick事件,該事件由handleAddNew所代表函數的欄位所定義,而在handleAddNew函數內部,props屬性需要由外界來賦值。會類似
<AddFriend addNew={...} />
這樣的形式呈現
以上的AddFriend組件是也是一個被嵌套組件,它和ShowList組件被一起嵌套在ParentContainer這個組件中。
於是,就來到了ParentContainer這個組件。
var ParentContainer = React.createClass({
getInitialState: function(){
return {
name: 'darren',
friends: ['a','b']
}
},
addFriend: function(friend){
this.setState({
this.setState({
friends: this.state.friends.concat([friend])
});
});
},
render: function(){
return (
<div>
<h3>Name: {this.state.name}</h3>
<AddFriend addNew={this.addFriend} />
<ShowList names={this.state.friends} />
</div>
)
}
});
被嵌套組件所需要的,都通過嵌套組件的state或函數獲得。
2.3 設置組件的屬性類型
在上面的AddFriend組件中,button是這樣的:<button onClick={this.handleAddNew}>Add Friend</button>
,button的點擊事件由handleAddNew函數決定,而handleAddNew函數由this.props.addNew(this.state.newFriend)
所決定,addNew函數是props屬性的函數類型欄位,是否可以定義呢?答案是可以。
var AddFriend = React.createClas({
getIntialState: function(){
return {
newFriend: ''
}
},
propTypes: {
addNew: React.PropTypes.func.isRequired
},
updateNewFriend: function(e){//更新就是為state的newFriend欄位賦上當前的值
this.setState({
newFriend: e.target.value
});
},
handleAddNew: function(){
this.props.addNew(this.state.newFriend);
this.setState({
newFriend: ''
});
},
render: function(){
return (
<div>
<input type="text" value={this.state.newFriend} onChange={this.updateNewFriend} />
<button onClick={this.handleAddNew}>Add Friend</button>
</div>
)
}
});
3、組件的生命周期
組件的生命周期又是怎樣的呢?
var FriendsConainer = React.createClass({
getInitialState: function(){
alert('getInitialState');
return {
name: 'darren'
}
},
componentWillMount: function(){},
componentDidMount: function(){},
componentWillReceiveProps: function(nextProps){},
componentWillUnmount: function(){},
render: function(){
return (
<div>
Hello, {this.state.name}
</div>
)
}
});
- componentWillMount:初次渲染之前調用一次
- componentDidMount:初次渲染之後調用,這時,可以訪問virtual DOM了
- componentWillReceiveProps:初次渲染不被調用,只有當props屬性值有變化才調用
- componentWillUnmount:從virutal DOM分離的時候調用