React Native之React速學(xué)教程(上)
本文出自《React Native學(xué)習(xí)筆記》系列文章。
React Native是基于React的,在開(kāi)發(fā)React Native過(guò)程中少不了的需要用到React方面的知識(shí)。雖然官方也有相應(yīng)的Document,但篇幅比較多,學(xué)起來(lái)比較枯燥。
通過(guò)《React Native之React速學(xué)教程》你可以對(duì)React有更系統(tǒng)和更深入的認(rèn)識(shí)。為了方便大家學(xué)習(xí),我將《React Native之React速學(xué)教程》分為上、中、下三篇,大家可以根據(jù)需要進(jìn)行閱讀學(xué)習(xí)。
概述
本篇為《React Native之React速學(xué)教程》的第一篇。本篇將從React的特點(diǎn)、如何使用React、JSX語(yǔ)法、組件(Component)以及組件的屬性,狀態(tài)等方面進(jìn)行講解。
What's React
React是一個(gè)用于組建用戶界面的JavaScript庫(kù),讓你以更簡(jiǎn)單的方式來(lái)創(chuàng)建交互式用戶界面。
- 當(dāng)數(shù)據(jù)改變時(shí),React將高效的更新和渲染需要更新的組件。聲明性視圖使你的代碼更可預(yù)測(cè),更容易調(diào)試。
- 構(gòu)建封裝管理自己的狀態(tài)的組件,然后將它們組裝成復(fù)雜的用戶界面。由于組件邏輯是用JavaScript編寫(xiě)的,而不是模板,所以你可以輕松地通過(guò)您的應(yīng)用程序傳遞豐富的數(shù)據(jù),并保持DOM狀態(tài)。
- 一次學(xué)習(xí)隨處可寫(xiě),學(xué)習(xí)React,你不僅可以將它用于Web開(kāi)發(fā),也可以用于React Native來(lái)開(kāi)發(fā)Android和iOS應(yīng)用。
不是模板卻比模板更加靈活:

心得:上圖是GitHub Popular的首頁(yè)截圖,這個(gè)頁(yè)面是通過(guò)不同的組件組裝而成的,組件化的開(kāi)發(fā)模式,使得代碼在更大程度上的到復(fù)用,而且組件之間對(duì)的組裝很靈活。
Get Started
使用React之前需要在頁(yè)面引入如下js庫(kù) 。
- react.js
- react-dom.js
- browser.min.js
上面一共列舉了三個(gè)庫(kù): react.js 、react-dom.js 和 browser.min.js ,它們必須首先加載。其中,react.js 是 React 的核心庫(kù),react-dom.js 是提供與 DOM 相關(guān)的功能,browser.min.js 的作用是將 JSX 語(yǔ)法轉(zhuǎn)為 JavaScript 語(yǔ)法,這一步很消耗時(shí)間,實(shí)際上線的時(shí)候,應(yīng)該將它放到服務(wù)器完成。
你可以從React官網(wǎng)下載這些庫(kù),也可以將其下載到本地去使用。
心得:在做React Native開(kāi)發(fā)時(shí),這些庫(kù)作為React Native核心庫(kù)已經(jīng)被初始化在node_modules目錄下,所以不需要單獨(dú)下載。
使用React
解壓從上述地址下載的壓縮包,在根目錄中創(chuàng)建一個(gè)包含以下內(nèi)容的 “helloworld.html” 。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="build/react.js"></script>
<script src="build/react-dom.js"></script>
<script src="https://npmcdn.com/babel-core@5.8.38/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
</script>
</body>
</html>
在 JavaScript 代碼里寫(xiě)著 XML 格式的代碼稱(chēng)為 JSX,下文會(huì)介紹。為了把 JSX 轉(zhuǎn)成標(biāo)準(zhǔn)的 JavaScript,我們用<script type="text/babel">標(biāo)簽,然后通過(guò)Babel轉(zhuǎn)換成在瀏覽器中真正執(zhí)行的內(nèi)容。
ReactDOM.render()
ReactDOM.render 是 React 的最基本方法,用于將模板轉(zhuǎn)為 HTML 語(yǔ)言,并插入指定的 DOM 節(jié)點(diǎn)。
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
上述代碼的作用是將<h1>Hello, world!</h1>插入到元素id為example的容器中。
JSX
JSX 是一個(gè)看起來(lái)很像 XML 的 JavaScript 語(yǔ)法擴(kuò)展。
每一個(gè)XML標(biāo)簽都會(huì)被JSX轉(zhuǎn)換工具轉(zhuǎn)換成純JavaScript代碼,使用JSX,組件的結(jié)構(gòu)和組件之間的關(guān)系看上去更加清晰。
JSX并不是React必須使用的,但React官方建議我們使用 JSX , 因?yàn)樗芏x簡(jiǎn)潔且我們熟知的包含屬性的樹(shù)狀結(jié)構(gòu)語(yǔ)法。
Usage:
React.render(//使用JSX
<div>
<div>
<div>content</div>
</div>
</div>,
document.getElementById('example')
);
React.render(//不使用JSX
React.createElement('div', null,
React.createElement('div', null,
React.createElement('div', null, 'content')
)
),
document.getElementById('example')
);
HTML標(biāo)簽 與 React組件 對(duì)比
React 可以渲染 HTML 標(biāo)簽 (strings) 或 React 組件 (classes)。
要渲染 HTML 標(biāo)簽,只需在 JSX 里使用小寫(xiě)字母開(kāi)頭的標(biāo)簽名。
var myDivElement = <div className="foo" />;
React.render(myDivElement, document.body);
要渲染 React 組件,只需創(chuàng)建一個(gè)大寫(xiě)字母開(kāi)頭的本地變量。
var MyComponent = React.createClass({/*...*/});
var myElement = <MyComponent someProperty={true} />;
React.render(myElement, document.body);
提示:
- React 的 JSX 里約定分別使用首字母大、小寫(xiě)來(lái)區(qū)分本地組件的類(lèi)和 HTML 標(biāo)簽。
- 由于 JSX 就是 JavaScript,一些標(biāo)識(shí)符像 class 和 for 不建議作為 XML 屬性名。作為替代, React DOM 使用 className 和 htmlFor 來(lái)做對(duì)應(yīng)的屬性。
JavaScript 表達(dá)式
屬性表達(dá)式
要使用 JavaScript 表達(dá)式作為屬性值,只需把這個(gè)表達(dá)式用一對(duì)大括號(hào) ({}) 包起來(lái),不要用引號(hào) ("")。
// 輸入 (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ''} />;
// 輸出 (JS):
var person = React.createElement(
Person,
{name: window.isLoggedIn ? window.name : ''}
);
子節(jié)點(diǎn)表達(dá)式
同樣地,JavaScript 表達(dá)式可用于描述子結(jié)點(diǎn):
// 輸入 (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// 輸出 (JS):
var content = React.createElement(
Container,
null,
window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);
注釋
JSX 里添加注釋很容易;它們只是 JS 表達(dá)式而已。你只需要在一個(gè)標(biāo)簽的子節(jié)點(diǎn)內(nèi)(非最外層)用 {} 包圍要注釋的部分。
class ReactDemo extends Component {
render() {
return (
<View style={styles.container}>
{/*標(biāo)簽子節(jié)點(diǎn)的注釋*/}
<Text style={styles.welcome}
//textAlign='right'
textShadowColor='yellow'
/*color='red'
textShadowRadius='1'*/
>
React Native!
</Text>
</View>
);
}
}
心得:在標(biāo)簽節(jié)點(diǎn)以外注釋?zhuān)屯ǔ5淖⑨屖且粯拥?,多行用?**/” 單行用“//”;
JSX延展屬性
不要試圖去修改組件的屬性
不推薦做法:
var component = <Component />;
component.props.foo = x; // 不推薦
component.props.bar = y; // 不推薦
這樣修改組件的屬性,會(huì)導(dǎo)致React不會(huì)對(duì)組件的屬性類(lèi)型(propTypes)進(jìn)行的檢查。從而引發(fā)一些預(yù)料之外的問(wèn)題。
推薦做法:
var component = <Component foo={x} bar={y} />;
延展屬性(Spread Attributes)
你可以使用 JSX 的新特性 - 延展屬性:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
傳入對(duì)象的屬性會(huì)被復(fù)制到組件內(nèi)。
它能被多次使用,也可以和其它屬性一起用。注意順序很重要,后面的會(huì)覆蓋掉前面的。
var props = { foo: 'default' };
var component = <Component {...props} foo={'override'} />;
console.log(component.props.foo); // 'override'
上文出現(xiàn)的... 標(biāo)記被叫做延展操作符(spread operator)已經(jīng)被 ES6 數(shù)組 支持。
Component
React 允許將代碼封裝成組件(component),然后像插入普通 HTML 標(biāo)簽一樣,在網(wǎng)頁(yè)中插入這個(gè)組件。
var HelloMessage = React.createClass({
render: function() {
return <h1>Hello {this.props.name}</h1>;
}
});
ReactDOM.render(
<HelloMessage name="John" />,
document.getElementById('example')
);
上面代碼中,變量 HelloMessage 就是一個(gè)組件類(lèi)。模板插入 <HelloMessage />時(shí),會(huì)自動(dòng)生成 HelloMessage 的一個(gè)實(shí)例。所有組件類(lèi)都必須有自己的 render 方法,用于輸出組件。
注意
- 組件類(lèi)的第一個(gè)字母必須大寫(xiě)。
- 組件類(lèi)只能包含一個(gè)頂層標(biāo)簽。
組件的屬性(props)
我們可以通過(guò)this.props.xx的形式獲取組件對(duì)象的屬性,對(duì)象的屬性可以任意定義,但要避免與JavaScript關(guān)鍵字沖突。
遍歷對(duì)象的屬性:
this.props.children會(huì)返回組件對(duì)象的所有屬性。
React 提供一個(gè)工具方法 React.Children 來(lái)處理 this.props.children 。我們可以用 React.Children.map或React.Children.forEach 來(lái)遍歷子節(jié)點(diǎn)。
React.Children.map
array React.Children.map(object children, function fn [, object thisArg])
該方法會(huì)返回一個(gè)array。
React.Children.forEach
React.Children.forEach(object children, function fn [, object thisArg])
Usage:
var NotesList = React.createClass({
render: function() {
return (
<ol>
{
React.Children.map(this.props.children, function (child) {
return <li>{child}</li>;
})
}
</ol>
);
}
});
ReactDOM.render(
<NotesList>
<span>hello</span>
<span>world</span>
</NotesList>,
document.body
);
PropTypes
組件的屬性可以接受任意值,字符串、對(duì)象、函數(shù)等等都可以。有時(shí),我們需要一種機(jī)制,驗(yàn)證別人使用組件時(shí),提供的參數(shù)是否符合要求。
組件類(lèi)的PropTypes屬性,就是用來(lái)驗(yàn)證組件實(shí)例的屬性是否符合要求。
var MyTitle = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
上面的Mytitle組件有一個(gè)title屬性。PropTypes 告訴 React,這個(gè) title 屬性是必須的,而且它的值必須是字符串?,F(xiàn)在,我們?cè)O(shè)置 title 屬性的值是一個(gè)數(shù)值。
var data = 123;
ReactDOM.render(
<MyTitle title={data} />,
document.body
);
這樣一來(lái),title屬性就通不過(guò)驗(yàn)證了。控制臺(tái)會(huì)顯示一行錯(cuò)誤信息。
Warning: Failed propType: Invalid prop `title` of type `number` supplied to `MyTitle`, expected `string`.
更多的PropTypes設(shè)置,可以查看官方文檔。
此外,getDefaultProps 方法可以用來(lái)設(shè)置組件屬性的默認(rèn)值。
var MyTitle = React.createClass({
getDefaultProps : function () {
return {
title : 'Hello World'
};
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
ReactDOM.render(
<MyTitle />,
document.body
);
上面代碼會(huì)輸出"Hello World"。
ref 屬性(獲取真實(shí)的DOM節(jié)點(diǎn))
組件并不是真實(shí)的 DOM 節(jié)點(diǎn),而是存在于內(nèi)存之中的一種數(shù)據(jù)結(jié)構(gòu),叫做虛擬 DOM (virtual DOM)。只有當(dāng)它插入文檔以后,才會(huì)變成真實(shí)的 DOM 。根據(jù) React 的設(shè)計(jì),所有的 DOM 變動(dòng),都先在虛擬 DOM 上發(fā)生,然后再將實(shí)際發(fā)生變動(dòng)的部分,反映在真實(shí) DOM上,這種算法叫做 DOM diff ,它可以極大提高網(wǎng)頁(yè)的性能表現(xiàn)。
但是,有時(shí)需要從組件獲取真實(shí) DOM 的節(jié)點(diǎn),這時(shí)就要用到 ref 屬性。
var MyComponent = React.createClass({
handleClick: function() {
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>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById('example')
);
上面代碼中,組件 MyComponent 的子節(jié)點(diǎn)有一個(gè)文本輸入框,用于獲取用戶的輸入。這時(shí)就必須獲取真實(shí)的 DOM 節(jié)點(diǎn),虛擬 DOM 是拿不到用戶輸入的。為了做到這一點(diǎn),文本輸入框必須有一個(gè) ref 屬性,然后 this.refs.[refName] 就會(huì)返回這個(gè)真實(shí)的 DOM 節(jié)點(diǎn)。
需要注意的是,由于 this.refs.[refName] 屬性獲取的是真實(shí) DOM ,所以必須等到虛擬 DOM 插入文檔以后,才能使用這個(gè)屬性,否則會(huì)報(bào)錯(cuò)。上面代碼中,通過(guò)為組件指定 Click 事件的回調(diào)函數(shù),確保了只有等到真實(shí) DOM 發(fā)生 Click 事件之后,才會(huì)讀取 this.refs.[refName] 屬性。
React 組件支持很多事件,除了 Click 事件以外,還有 KeyDown 、Copy、Scroll 等,完整的事件清單請(qǐng)查看官方文檔。
心得:ref屬性在開(kāi)發(fā)中使用頻率很高,使用它你可以獲取到任何你想要獲取的組件的對(duì)象,有個(gè)這個(gè)對(duì)象你就可以靈活地做很多事情,比如:讀寫(xiě)對(duì)象的變量,甚至調(diào)用對(duì)象的函數(shù)。
state
上文講到了props,因?yàn)槊總€(gè)組件只會(huì)根據(jù)props 渲染了自己一次,props 是不可變的。為了實(shí)現(xiàn)交互,可以使用組件的 state 。this.state 是組件私有的,可以通過(guò)getInitialState()方法初始化,通過(guò)調(diào)用 this.setState() 來(lái)改變它。當(dāng) state 更新之后,組件就會(huì)重新渲染自己。
render() 方法依賴(lài)于 this.props 和 this.state ,框架會(huì)確保渲染出來(lái)的 UI 界面總是與輸入( this.props 和 this.state )保持一致。
初始化state
通過(guò)getInitialState()方法初始化state,在組件的生命周期中僅執(zhí)行一次,用于設(shè)置組件的初始化 state 。
getInitialState:function(){
return {favorite:false};
}
更新 state
通過(guò)this.setState()方法來(lái)更新state,調(diào)用該方法后,React會(huì)重新渲染相關(guān)的UI。
this.setState({favorite:!this.state.favorite});
Usage:
var FavoriteButton=React.createClass({
getInitialState:function(){
return {favorite:false};
},
handleClick:function(event){
this.setState({favorite:!this.state.favorite});
},
render:function(){
var text=this.state.favorite? 'favorite':'un favorite';
return (
<div type='button' onClick={this.handleClick}>
You {text} this. Click to toggle.
</div>
);
}
});
上面代碼是一個(gè) FavoriteButton 組件,它的 getInitialState 方法用于定義初始狀態(tài),也就是一個(gè)對(duì)象,這個(gè)對(duì)象可以通過(guò) this.state 屬性讀取。當(dāng)用戶點(diǎn)擊組件,導(dǎo)致?tīng)顟B(tài)變化,this.setState 方法就修改狀態(tài)值,每次修改以后,自動(dòng)調(diào)用 this.render 方法,再次渲染組件。
心得:由于 this.props 和 this.state 都用于描述組件的特性,可能會(huì)產(chǎn)生混淆。一個(gè)簡(jiǎn)單的區(qū)分方法是,this.props 表示那些一旦定義,就不再改變的特性,而 this.state 是會(huì)隨著用戶互動(dòng)而產(chǎn)生變化的特性。
參考
React's official site
React on ES6+
About
本文出自《React Native學(xué)習(xí)筆記》系列文章。
了解更多,可以關(guān)注我的GitHub
@http://jiapenghui.com
推薦閱讀
- React Native 學(xué)習(xí)筆記
- [Reac Native布局詳細(xì)指南](https://github.com/crazycodeboy/RNStudyNotes/tree/master/React Native布局/React Native布局詳細(xì)指南/React Native布局詳細(xì)指南.md)
- React Native調(diào)試技巧與心得
- React Native發(fā)布APP之簽名打包APK
- React Native應(yīng)用部署、熱更新-CodePush最新集成總結(jié)