ReactJS入門學習二
ReactJS入門學習二
閱讀目錄
React的背景和基本原理
在web開發中,我們總需要將變化的數據實時反應到UI上,這時就需要對DOM進行操作,復雜或頻繁的對DOM操作是性能瓶頸產生的原因,React為此引入了虛擬的DOM的機制,在瀏覽器端使用javascript實現了一套DOM API,基於React開發時所有的DOM構造都是通過虛擬的DOM進行的,每當數據有變化時,React都會重新構建整個DOM樹,然后React將當前的整個DOM樹與之前的DOM樹進行對比,得到變化的部分,然后將變化的部分進行實際的瀏覽器的DOM更新,而且React能夠批處理虛擬的DOM刷新,在一個事件循環內的兩次數據的變化會被合並,比如你連續先將節點內容A變成B,然后將B變成A,React會認為UI不發生任何變化。盡管每一次都需要構造完整的虛擬DOM樹,但是虛擬DOM樹是內存數據,性能是非常高的,而對實際的DOM進行操作僅僅是Diff部分,因此可以提高性能。在保證性能的同時,我們將不再需要關注某個數據的變化如何更新到一個或者多個具體的DOM元素,而只需要關心在任意一個數據狀態下,整個界面是如何Render的。
React組件化:
虛擬DOM不僅帶來了簡單的UI開發邏輯,同時也帶來了組件開發的思想,所謂組件,即封裝起來的具有獨立功能的UI部件,React推薦以組件的方式去重寫思考UI構成,將UI上的每一個功能相對獨立的模塊定義成組件。然后將小組件通過嵌套的方式變成大組件,最終構成完整的UI構建。如果說MVC的思想讓我們做到表現,數據,控制的分離,那么React則以組件化的思考方式則帶來了UI模塊間的分離,比如我上一篇文章介紹的網站頁面一樣:如下圖:

導航條,側邊欄及內容區分別為不同的小組件通過嵌套的方式變成一個完整的UI界面構建。
React組件應該有如下特征:
- 可組合:一個組件易於與其他組件一起使用,或者說嵌套在另一個組件內部。
- 可重用:每個組件都具有獨立的功能,它可以被使用在多個UI場景。
- 可維護:每個小的組件僅僅包含自身的邏輯,更容易理解及維護。
下面我們首先使用React時候需要引用如下JS,react.js和JSXTransformer.js,下面的HTML代碼如下:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="js/react.js"></script>
<script src="js/JSXTransformer.js"></script>
</head>
<body>
<div id = "demo"></div>
<script type="text/jsx">
// code
</script>
</body>
</html>
理解React.render()
React.render()是用於將模版轉為HTML語言,並且插入指定的DOM節點。
如下代碼:
React.render(
<h1>hello,world!</h1>,
document.getElementById("demo")
);
頁面生成HTML結構如下:
頁面顯示如下:

什么是JSX?
React的核心機制就是實現了一個虛擬DOM,利用虛擬DOM來減少對實際DOM的操作從而提升性能,組件DOM結構就是映射到這個虛擬的DOM上,React在這個虛擬DOM上實現了一個diff算法,當要更新組件的時候,會通過diff尋找要變更的DOM節點,再把這個修改更新到瀏覽器實際的DOM節點上,所以實際上不是真的渲染整個DOM樹,這個虛擬的DOM是一個純粹的JS數據結構,所以性能比原生DOM會提高很多;
虛擬DOM也可以通過Javascript來創建,比如如下代碼:
var child1 = React.createElement('li',null,'First Text Content');
var child2 = React.createElement('li',null,'second Text Content');
var root = React.createElement('ul',{className:'test'},child1,child2);
React.render(root,document.getElementById("demo"));
在頁面中渲染成結構變成如下:

在頁面顯示如下:

但是這樣編寫代碼可讀性不好,於是React就出現了JSX,使用如下的方式創建虛擬DOM;
var root = (
<ul className="test">
<li>First Text Content</li>
<li>second Text Content</li>
</ul>
);
React.render(root,document.getElementById("demo"));
上面2種方式實現的效果都一樣,可能很多人會認為引入一個JSX的源碼進來會影響頁面性能,在這里我們只是為了測試而已,如果真的在開發項目中,JSX在產品打包階段會編譯成純粹的javascript,JSX語法不會帶來任何性能影響的。
為什么要使用JSX?
前端開發最基本的功能是展示數據,因此很多框架都引入了模版引擎,如果我們不使用模版的話,那么我們需要手動並接一個很長的html字符串,並且這樣並接很容易出錯,代碼也不太美觀,且最重要的是展示邏輯和業務邏輯沒有得到足夠的分離(使用MVC思想考慮);因此React就發明了JSX;
JSX的語法
JSX允許HTML與javascript混寫,如下代碼:
var names = ['longen1','longen2','longen3'];
React.render(
<div className="aa">
{
names.map(function(name){
return <p>Hello,{name}</p>
})
}
</div>,document.getElementById("demo")
);
JSX的語法規則是:遇到HTML標簽(以<開頭),就是用HTML的規則解析,遇到代碼塊(以{開頭),就用jSX的語法規則解析;上面的代碼生成如下:

理解this.props
Props表示的是組件自身的屬性,是父層節點傳遞給子層節點的一些屬性或者數據,如下代碼:
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
React.render(<HelloMessage name="John" />, document.getElementById("demo"));
理解this.props.children
this.props對象的屬性與組件的屬性一一對應,但是有一個列外,就是this.props.children屬性。它表示的是組件的所有子節點;
如下代碼:
var NotesList = React.createClass({
render: function() {
return (
<ol>
{
this.props.children.map(function (child) {
return <li>{child}</li>
})
}
</ol>
);
}
});
React.render(
<NotesList>
<span>hello</span>
<span>world</span>
</NotesList>,
document.getElementById("demo")
);
在頁面顯示如下:

理解React.createClass方法
React允許將代碼封裝成組件,然后像普通的HTML一樣插入,React.createClass方法用於生成一個組件類;如下代碼:
var NodeList = React.createClass({
render: function() {
return <h1>Hello {this.props.name}</h1>;
}
});
React.render(
<NodeList name="John" />,
document.getElementById('demo')
);
如上代碼;NodeList就是一個組件類,模版插入<NodeList />時,會自動生成一個NodeList的實列;所有組件都必須有一個render()方法,用於輸出組件;如上<NodeList name=”John”/>就是加入了一個屬性name,組件上的屬性我們可以使用this.props對象上獲取,比如上面的name屬性就可以通過this.props.name讀取;
在頁面顯示如下:

理解this.state
this.state是組件私有的,我們可以通過this.setState()來改變它,那么組件會重新渲染下自己;如下代碼:
var LikeButton = React.createClass({
getInitialState: function() {
return {liked: false};
},
handleClick: function(event) {
this.setState({liked: !this.state.liked});
},
render: function() {
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});
React.render(
<LikeButton />,
document.getElementById('demo')
);
如上代碼 先使用 getInitialState() 方法用於定義初始狀態(且只執行一次),當用戶點擊時候,調用handleClick函數,使用this.setState來動態的更改值,每次更改后,會自動調用render()進行渲染組件。
this.props與this.state的區別?
this.props是指屬性從父節點元素繼承過來的,且屬性不可以更改的。
this.state 是指可以動態更改的;是組件的私有的;
Refs和findDOMNode()
為了同瀏覽器交互,我們有時候需要獲取真實的DOM節點,我們可以通過React的 React.findDOMNode(component)獲取到組件中真實的DOM;
如下代碼:
var MyComponent = React.createClass({
handleClick: function() {
React.findDOMNode(this.refs.myTextInput).focus();
},
render: function() {
return (
<div>
<input type="text" ref="myTextInput" />
<input
type="button"
value="Focus the text input"
onClick={this.handleClick}
/>
</div>
);
}
});
React.render(
<MyComponent />,
document.getElementById('demo')
);
組件MyComponent的子節點有一個文本輸入框,點擊按鈕后,把焦點放在輸入框內,這時候我們需要先獲取到MyComponent組件內的真實的DOM節點,為了做到這點,文本輸入框必須有一個ref屬性,然后 this.refs.[refName]就指向與這個虛擬的DOM子節點,最后通過React.findDOMNode獲取真實的DOM節點。
理解React.createElement
參數:type(string), config(object), children(子節點)
如下代碼
var CommentBox = React.createClass({
displayName: 'CommentBox',
render: function(){
return (
React.createElement('div',{className:'commentBox'},"Hello,world!I am a CommentBox")
);
}
});
React.render(
React.createElement(CommentBox,null),document.getElementById("demo")
);
在頁面上顯示如下:

理解React.PropTypes
驗證使用組件時,提供的參數是否符合要求,組件的屬性可以接受任意值,字符串,對象,函數都可以;比如如下代碼,驗證組件中的 title屬性是否是字符串;如下代碼:
var MyTitle = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired
},
render: function() {
return this.props.title == 1234 ? <p>是一個字符串</p> : null
}
});
var data = "123";
React.render(
<MyTitle title="1234" />,document.getElementById("demo")
);
可以看到,在頁面打印出是一個字符串;
理解React.isValidElement
參數object
判斷參數是否是一個合法的的ReactElement,並返回Boolean值;如下代碼測試
var Longen = React.createClass({
render: function() {
return <p>123</p>
}
});
var test = <Longen/>,
test2 = '<Longen/>';
console.log(React.isValidElement(test)); //true
console.log(React.isValidElement(test2)); //false
如何在JSX中如何使用事件
我們以前編寫事件如下:
<button onclick="checkAndSubmit(this.form)">Submit</button>
這樣編寫代碼對於簡單的html頁面時沒有問題,但是對於復雜的的頁面,我們可以使用如下javascript來綁定事件:我們可以引用jquery;
$('#id').on('click', this.checkAndSubmit.bind(this));
但是在JSX中我們可以如下綁定事件:
<input type="text" value={value} onChange={this.handleChange} />
在JSX中我們不需要關心什么時候去移除事件綁定,因為React會在對應的真實的DOM節點移除就自動解除了事件綁定;
React並不會真正綁定事件到每一個元素上,而是采用事件代理的方式,在根節點document上為每種事件添加唯一的listener,然后通過事件的target找到真實的觸發目標元素,這樣從觸發元素到頂層節點之間的所有節點如果有綁定這個事件的話,React都會觸發對應的事件函數,這就是React模擬事件系統的基本原理。
盡管整個事件系統由React管理,但是其API和使用方法與原生事件一致。這種機制確保了跨瀏覽器的一致性:在所有瀏覽器(IE9及以上)都可以使用符合W3C標准的API,包括stopPropagation(),preventDefault()等等。對於事件的冒泡(bubble)和捕獲(capture)模式也都完全支持。
下面我們來做一個demo,來使用下JSX下的事件如下使用:
var Input = React.createClass({
getInitialState: function() {
return {value: 'Hello!'};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function () {
var value = this.state.value;
return (
<div>
<input type="text" value={value} onChange={this.handleChange} />
<p>{value}</p>
</div>
);
}
});
React.render(<Input/>, document.getElementById("demo"));
如上是一個input輸入框,當我們不斷在輸入框的值的時候,<p>標簽的內容也會跟隨的變化;
如何在JSX中如何使用樣式
大部分的時候我們都是把樣式寫到css文件內,但是有時候我們也可以將樣式寫到JSX中,在JSX中使用樣式和真實的樣式也很類似,也是通過style屬性來定義的,但是和真實DOM不同的是,屬性值不能是字符串,而一定要是對象,比如如下:
<div style={{color: '#ff0000', fontSize: '14px'}}>Hello World.</div>
我們可以看到JSX中使用的雙大括號,其實第一個大括號的含義是JSX的語法,第二個大括號的含義是一個對象;我們也可以如下寫:
var style = {
color: '#ff0000',
fontSize: '14px'
};
<div style={style}>HelloWorld.</div>

