React入門
本文介紹React的相關(guān)概念,使用方式。目的是使讀者對React有一個大致的了解,以便于能夠更加深入的學(xué)習(xí)、快速上手React。學(xué)習(xí)React要掌握其中的幾個重要的概念JSX、元素(事件處理)、組件(props、state、ref、受控組件、生命周期)。
React優(yōu)缺點
React是一個前端框架,學(xué)習(xí)框架必然要了解框架的優(yōu)缺點、以便根據(jù)不同的項目采用合適的框架。
優(yōu)點:
- 能夠?qū)崿F(xiàn)服務(wù)器端的渲染,便于搜索引擎優(yōu)化。
- 能夠很好的和現(xiàn)有的代碼結(jié)合。React只是MVC中的View層,對于其他的部分并沒有硬性要求。
- 組件化的開發(fā)方式,能夠更好的復(fù)用代碼、更好的定位bug。
- 使用virtual dom,性能很好
缺點:
- 不是完整的開發(fā)框架需要加上其他東西才能寫大型應(yīng)用。
JSX
JSX是JavaScript Syntax eXtension的縮寫,它是一種在JavaScript中嵌入XML的React語法,類似于E4X。React推薦使用JSX來描述用戶界面
JSX語法需要寫在<script type="text/babel"></script>標(biāo)簽中或者使用babel轉(zhuǎn)碼器進(jìn)行轉(zhuǎn)碼
JSX基本語法規(guī)則:遇到 HTML 標(biāo)簽(以 < 開頭),就用 HTML 規(guī)則解析;遇到代碼塊(以 { 開頭),就用 JavaScript 規(guī)則解析。
JSX 允許直接在模板插入 JavaScript 變量。如果這個變量是一個數(shù)組,則會展開這個數(shù)組的所有成員
<script type="text/babel">
var arr = [
<h1>Hello world!</h1>,
<h2>React is awesome</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
</script>
元素
元素是構(gòu)成React應(yīng)用的最小單位,React中元素時普通的對象。React DOM可以確保瀏覽器DOM的數(shù)據(jù)內(nèi)容與React元素保持一致。
const element = <h1>Hello, world</h1>;
// 使用ReactDOM.render渲染到頁面上
ReactDOM.render(
element,
document.getElementById('root')
);
當(dāng)元素被創(chuàng)建之后,是無法改變其內(nèi)容或?qū)傩缘?。如果想要改變需要將它傳入ReactDOM.render()方法中。(react只會渲染必要的部分)
元素除了可以是DOM元素外,還可以是自定義的組件。
事件處理
駝峰式命名,使用函數(shù)名作為事件處理函數(shù),需要使用e.preventDefault()來阻止默認(rèn)行為,不能直接使用return false阻止返回值。
<script type="text/babel">
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
e.preventDefault();
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
</script>
e是一個合成事件不需要考慮跨瀏覽器的兼容性問題。
使用實驗性的屬性初始化器語法來綁定this
<script type="text/babel">
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
</script>
組件
顧明思議是用來將UI切分成可復(fù)用且獨立的部件。
函數(shù)、類定義組件:
<script type="text/babel">
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
const element = <Welcome name="Sara" />;
</script>
組件之間可以像普通標(biāo)簽一樣嵌套組合成新元素、新組件。
style屬性的設(shè)置不能寫成:style="opacity:{this.state.opacity};",而要寫成style={{opacity: this.state.opacity}}。因為React組件樣式是一個對象,所以第一重大括號表示這是JS語法,第二重大括號表示樣式對象。
props
class 屬性需要寫成 className ,for 屬性需要寫成 htmlFor ,這是因為 class 和 for 是 JavaScript 的保留字。
this.props 對象的屬性與組件的屬性一一對應(yīng),但是有一個例外,就是 this.props.children 屬性。它表示組件的所有子節(jié)點
this.props.children 的值有三種可能:如果當(dāng)前組件沒有子節(jié)點,它就是 undefined ;如果有一個子節(jié)點,數(shù)據(jù)類型是 object ;如果有多個子節(jié)點,數(shù)據(jù)類型就是 array 。所以,處理 this.props.children 的時候要小心。
React 提供一個工具方法 React.Children 來處理 this.props.children 。我們可以用 React.Children.map 來遍歷子節(jié)點,而不用擔(dān)心 this.props.children 的數(shù)據(jù)類型是 undefined 還是 object。
props的值不能夠被改變。
使用propTypes來驗證別人使用組件時,提供的參數(shù)是否符合要求
使用defaultProps設(shè)置默認(rèn)值
Greeting.propTypes = {
title: React.PropTypes.string.isRequired,
//限制為一個子代
children: PropTypes.element.isRequired
};
Greeting.defaultProps = {
name:"test"
};
state
每當(dāng)組件的狀態(tài)state發(fā)生改變時,組件就可以自動的更新。
state在構(gòu)造函數(shù)中初始化,使用書面語的形式,即this.state = {}
狀態(tài)的更新需要使用setState(可接收對象或函數(shù)(參數(shù)是上一個狀態(tài)、props))函數(shù),不能直接使用書面語(this.state.comment = "")
this.props 和 this.state 可能是異步更新的,不能直接依靠它們來計算state,而應(yīng)該是使用函數(shù)的形式。
列表 & Keys
Keys可以在DOM中的某些元素被增加或刪除的時候幫助React識別哪些元素發(fā)生了變化。因此你應(yīng)當(dāng)給數(shù)組中的每一個元素賦予一個確定的標(biāo)識。
key應(yīng)該在數(shù)組中被明確出來,可以使用id或索引來作為key
<script type="text/babel">
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
</script>
受控組件
在HTML當(dāng)中,像<input>,<textarea>, 和 <select>這類表單元素會維持自身狀態(tài),并根據(jù)用戶輸入進(jìn)行更新。但在React中,可變的狀態(tài)通常保存在組件的狀態(tài)屬性中,并且只能用 setState(). 方法進(jìn)行更新.
因此使用“受控組件”的形式來編寫表單組件(值統(tǒng)一寫在value屬性中,更新方法寫在onChange函數(shù)中)
多個受控組件的統(tǒng)一解決方案,動態(tài)確定state的屬性
<script type="text/babel">
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
Is going:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Number of guests:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
</script>
非受控組件
在受控組件中,表單數(shù)據(jù)由 React 組件處理。如果讓表單數(shù)據(jù)由 DOM 處理時,替代方案為使用非受控組件。(使用ref獲取DOM并取值(必須要等到DOM插入后才能獲得DOM),使用defaultValue設(shè)置默認(rèn)值)
<script type="text/babel">
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
defaultValue="Bob"
type="text"
ref={(input) => this.input = input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
</script>
狀態(tài)提升
如果多個組件中有需要共用的狀態(tài),應(yīng)該將該狀態(tài)設(shè)置在父組件上,來實現(xiàn)狀態(tài)共享。
<script type="text/babel">
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return '';
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}
handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}
handleFahrenheitChange(temperature) {
this.setState({scale: 'f', temperature});
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
</script>
生命周期
組件的生命周期分成三個狀態(tài):
- Mounting:已插入真實 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真實 DOM
React 為每個狀態(tài)都提供了兩種處理函數(shù),will 函數(shù)在進(jìn)入狀態(tài)之前調(diào)用,did 函數(shù)在進(jìn)入狀態(tài)之后調(diào)用,三種狀態(tài)共計五種處理函數(shù)。
- componentWillMount()
- componentDidMount()
- componentWillUpdate(object nextProps, object nextState)
- componentDidUpdate(object prevProps, object prevState)
- componentWillUnmount()
此外,React 還提供兩種特殊狀態(tài)的處理函數(shù)。
- componentWillReceiveProps(object nextProps):已加載組件收到新的參數(shù)時調(diào)用
- shouldComponentUpdate(object nextProps, object nextState):組件判斷是否重新渲染時調(diào)用
參考文獻(xiàn):
React入門實例教程——阮一峰
React文檔15.6
React優(yōu)缺點