React簡介
React是一個用于構(gòu)建用戶界面的JavaScript庫祈惶,主要有以下幾個特點:
- 聲明式設計--React采用聲明范式,可以輕松描述應用
- 高效--React通過對DOM的模擬搓译,最大限度地減少與DOM的交互
- 靈活--React可以與已知的庫或框架很好地配合
- JSX--JSX是JavaScript語法的擴展悲柱。React開發(fā)不一定使用JSX,但建議使用
- 組件--通過React構(gòu)建組件些己,是的代碼更加容易得到復用豌鸡,能夠更好地應用在大項目的開發(fā)中
- 單向響應的數(shù)據(jù)流--React實現(xiàn)了單向響應的數(shù)據(jù)流,從而減少了重復代碼段标,這也是它為什么比傳統(tǒng)的數(shù)據(jù)綁定更簡單
React安裝
- 使用create-react-app快速構(gòu)建React開發(fā)環(huán)境
創(chuàng)建React App是開始構(gòu)建新的React單頁應用程序的最佳方式涯冠。它設置您的開發(fā)環(huán)境,以便您可以使用最新的JavaScript功能怀樟,提供一個很好的開發(fā)人員體驗功偿,并優(yōu)化您的應用程序的生產(chǎn)。
npm install -g create-react-app
create-react-app hello-world
cd hello-world
npm start
ReactDOM.render()
ReactDOM.render 是 React 的最基本方法往堡,用于將模板轉(zhuǎn)為 HTML 語言,并插入指定的 DOM 節(jié)點共耍。
ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') );
React JSX
JSX是一個看起來很像XML的JavaScript語法擴展虑灰,我們不是必須使用JSX,但是JSX有以下優(yōu)點:
- JSX執(zhí)行更快痹兜,因為它在編譯為JavaScript代碼后進行優(yōu)化
- 它是類型安全的穆咐,在編譯過程中就能發(fā)現(xiàn)錯誤
- 使用JSX編寫模板更加方便快捷
JSX的基本語法規(guī)則:<u>遇到 HTML 標簽(以 <
開頭),就用 HTML 規(guī)則解析字旭;遇到代碼塊(以 {
開頭)对湃,就用 JavaScript 規(guī)則解析。</u>
警告:
由于JSX比HTML更接近JavaScript遗淳,React DOM使用
camelCase
屬性命名約定而不是HTML屬性名稱拍柒。
/**使用JXS */
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
React組件和屬性
React允許將代碼封裝成組件,然后像插入普通的HTML標簽一樣养叛,在網(wǎng)頁中插入這個組件种呐,如下實例:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
上面的代碼中,變量App就是一個組件弃甥,所有的組件類都必須有自己的render方法爽室,用于輸出組件。
注意:組件名稱必須使用大寫淆攻,比如<div />就是一個HTML標簽阔墩,而
<App />
表示一個組件掉缺,并且要求組件類只能有一個頂層標簽,否則會報錯戈擒。
組件的屬性可以在組件類的 this.props
對象上獲取眶明,上面的例子中,name屬性是通過this.props.name來獲取的筐高。
注意:在添加屬性時搜囱, class 屬性需要寫成 className ,for 屬性需要寫成 htmlFor 柑土,這是因為 class 和 for 是 JavaScript 的保留字蜀肘。
this.props.children
this.props
對象的屬性與組件的屬性一一對應,但是有一個例外稽屏,就是 this.props.children
屬性扮宠。它表示組件的所有子節(jié)點。
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 );
上面代碼的 NoteList
組件有兩個 span
子節(jié)點狐榔,它們都可以通過 this.props.children
讀取坛增,運行結(jié)果如下。
這里需要注意薄腻, this.props.children
的值有三種可能:如果當前組件沒有子節(jié)點收捣,它就是 undefined
;如果有一個子節(jié)點,數(shù)據(jù)類型是 object
庵楷;如果有多個子節(jié)點罢艾,數(shù)據(jù)類型就是 array
。所以尽纽,處理 this.props.children
的時候要小心咐蚯。
React 提供一個工具方法 React.Children
來處理 this.props.children
。我們可以用 React.Children.map
來遍歷子節(jié)點弄贿,而不用擔心 this.props.children
的數(shù)據(jù)類型是 undefined
還是 object
春锋。更多的 React.Children
的方法,請參考官方文檔挎春。
組件狀態(tài)和生命周期
我們通常使用兩種數(shù)據(jù)來控制一個組件:props和state看疙。props是在父組件中指定的莱睁,而且一經(jīng)指定项阴,在被指定的組建的生命周期中則不改變厂庇,對于需要改變的數(shù)據(jù)形娇,我們需要使用state來處理黔漂。
通常情況下嚷狞,我們需要在constructor中初始化state夺溢,然后在需要修改的時候調(diào)用setState()方法甥捺。
/**加了狀態(tài)之后的定時器 */
class Clock extends Component {
constructor(props) {
super(props);
this.state = {date: new Date()}; // 設置初始狀態(tài)
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() { //在組件從 DOM 中移除的時候立刻被調(diào)用
clearInterval(this.timerID);
}
tick() {
this.setState({ // 組件狀態(tài)的更新
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
上面的例子,我們主要是通過以下幾步來實現(xiàn)的:
1)當
<Clock />
傳遞給ReactDOM.render()
React時渠旁,React調(diào)用組件的Clock
構(gòu)造函數(shù)攀例。由于Clock
需要顯示當前時間,它this.state
用包括當前時間的對象初始化顾腊。我們稍后將更新此狀態(tài)粤铭。2)React然后調(diào)用
Clock
組件的render()
方法。這是React如何了解應該在屏幕上顯示什么杂靶。React然后更新DOM以匹配Clock
渲染輸出梆惯。3)當
Clock
輸出插入到DOM中時,React調(diào)用componentDidMount()
生命周期鉤子吗垮。在它內(nèi)部垛吗,Clock
組件要求瀏覽器設置一個定時器,tick()
每秒鐘調(diào)用一次烁登。4)每秒瀏覽器調(diào)用該
tick()
方法怯屉。在其中,Clock
組件通過setState()
使用包含當前時間的對象調(diào)用來計劃UI更新饵沧。由于setState()
調(diào)用锨络,React知道狀態(tài)已更改,并render()
再次調(diào)用方法來了解應該在屏幕上顯示什么捷泞。這一次足删,this.state.date
在render()
方法中會有所不同,因此渲染輸出將包括更新的時間锁右。React相應地更新DOM。5)如果
Clock
組件從DOM中刪除讶泰,React會調(diào)用componentWillUnmount()
生命周期鉤子咏瑟,以便定時器停止。
如何正確使用狀態(tài)
不要直接修改狀態(tài)
例如痪署,這將不會重新渲染組件:
// Wrong
this.state.comment = 'Hello';
相反码泞,請使用setState()
:
// Correct
this.setState({comment: 'Hello'});
唯一可以指定this.state
值的地方是構(gòu)造函數(shù)。
狀態(tài)更新可能會異步
React可以將多個setState()
調(diào)用批處理為單個更新以實現(xiàn)性能狼犯。
因為this.props
和this.state
可能異步更新余寥,你不應該依賴它們的值來計算下一個狀態(tài)。
例如悯森,此代碼可能無法更新計數(shù)器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
要解決它宋舷,使用setState()
接受函數(shù)而不是對象的第二種形式。該函數(shù)將接收先前的狀態(tài)作為第一個參數(shù)瓢姻,并將應用更新時的props作為第二個參數(shù):
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
我們使用上面的箭頭函數(shù)祝蝠,但它也可以與常規(guī)函數(shù)一起使用:
// Correct
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
狀態(tài)更新已合并
每次調(diào)用setState()
時,React都會將您提供的對象合并到當前狀態(tài)。
例如绎狭,您的狀態(tài)可能包含幾個獨立變量:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
然后您可以通過單獨的setState()
調(diào)用獨立地更新它們:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
合并很淺,因為this.setState({comments})
保留this.state.posts
不變细溅,只是完全替換this.state.comments
。
組件的生命周期
組件的生命周期可分成三個狀態(tài):
- Mounting: 已插入真實DOM
- Updating: 正在被重新渲染
- Unmounting: 已移出真實DOM
生命周期的方法:(詳細說明儡嘶,可以參考官方文檔喇聊。)
- componentWillMount 在渲染前調(diào)用。
- componentDidMount : 在第一次渲染后調(diào)用蹦狂。之后組件已經(jīng)生成了對應的DOM結(jié)構(gòu)誓篱,可以通過this.getDOMNode()來進行訪問。 如果你想和其他JavaScript框架一起使用鸥咖,可以在這個方法中調(diào)用setTimeout, setInterval或者發(fā)送AJAX請求等操作(防止異部操作阻塞UI)燕鸽。
- componentWillReceiveProps 在組件接收到一個新的prop時被調(diào)用。這個方法在初始化render時不會被調(diào)用啼辣。
-
shouldComponentUpdate 返回一個布爾值啊研。在組件接收到新的props或者state時被調(diào)用。在初始化時或者使用forceUpdate時不被調(diào)用鸥拧。
可以在你確認不需要更新組件時使用党远。 - componentWillUpdate在組件接收到新的props或者state但還沒有render時被調(diào)用。在初始化時不會被調(diào)用富弦。
- componentDidUpdate 在組件完成更新后立即調(diào)用沟娱。在初始化時不會被調(diào)用。
- componentWillUnmount在組件從 DOM 中移除的時候立刻被調(diào)用腕柜。
處理事件與條件渲染
處理事件
使用React元素處理事件與處理DOM元素上的事件非常相似济似。有一些語法差異:
- React事件使用camelCase命名,而不是小寫命名盏缤。
- 使用JSX你傳遞一個函數(shù)作為事件處理程序砰蠢,而不是一個字符串。
另一個區(qū)別是唉铜,你不能返回false
以防止React中的默認行為台舱。您必須preventDefault
明確調(diào)用。
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() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}> // 傳遞一個函數(shù)作為事件處理程序
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
你必須注意this
在JSX回調(diào)中的意思潭流。在JavaScript中竞惋,類方法默認不綁定。如果你忘記綁定this.handleClick
并把它傳遞給onClick
灰嫉,在函數(shù)實際被調(diào)用時this
將會報undefined
的錯誤拆宛。
條件渲染
React中的條件渲染與JavaScript中的條件工作方式相同。使用類似if
的JavaScript運算符或條件運算符來創(chuàng)建表示當前狀態(tài)的元素熬甫,并讓React更新UI以匹配它們胰挑。
/**條件渲染 */
class LoginControl extends Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
)
}
}
function LoginButton(props) {
return (
<button onClick={props.onClick}>
Login
</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>
Logout
</button>
);
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
雖然聲明變量和使用if
語句是有條件地呈現(xiàn)組件的一種很好的方式蔓罚,但有時您可能想使用較短的語法。有幾種方法來內(nèi)聯(lián)JSX中的條件瞻颂,如下所述豺谈。
邏輯運算符
您可以在JSX中嵌入任何表達式,將它們括在大括號中贡这。這包括JavaScript邏輯&&
運算符茬末。它可以方便地有條件地包括一個元素:
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
它的工作原理同JavaScript,true && expression
總是解析為expression
盖矫,false && expression
總是解析為false
丽惭。
因此,如果條件是true
辈双,緊接著的元素&&
將出現(xiàn)在輸出中责掏。如果是false
,React將忽略并跳過它湃望。
帶條件運算符的If-else
用于有條件地內(nèi)聯(lián)元素的另一種方法是使用JavaScript條件運算符condition ? case1 : case2
换衬,當條件為true時,執(zhí)行case1,否則執(zhí)行case2证芭。
防止組件呈現(xiàn)
在極少數(shù)情況下瞳浦,您可能希望組件隱藏自身,即使它由另一個組件呈現(xiàn)废士。要做這個返回null
而不是其渲染輸出叫潦。
在下面的例子中,根據(jù)<WarningBanner />
被調(diào)用的prop的值來渲染warn
官硝。如果prop的值為false
矗蕊,則組件不呈現(xiàn):
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true}
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(prevState => ({
showWarning: !prevState.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
列表和Key值
列表
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
上述實例顯示1到5之間的數(shù)字的項目符號列表。但是運行此代碼時氢架,將會收到一條警告拔妥,指出應為列表項提供一個鍵〈锕浚“key”是創(chuàng)建元素列表時需要包含的特殊字符串屬性。如下:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}> //為列表項指定key值
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Key值
key幫助確定哪些項目已更改铺厨,已添加或已刪除缎玫。應該給數(shù)組中的元素賦予key以給元素一個穩(wěn)定的身份。
選擇key的最好方法是使用一個字符串解滓,它在其兄弟之間唯一標識一個列表項赃磨。通常,您會使用數(shù)據(jù)中的ID作為鍵:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
使用鍵提取組件:
如果提取一個ListItem
組件洼裤,則應該將該key保存<ListItem />
在數(shù)組中的元素上邻辉,而不是<li>
其ListItem
本身的根元素上。
正確使用:
function ListItem(props) {
// Correct! There is no need to specify the key here:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified inside the array.
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
一個好的經(jīng)驗法則就是map()
調(diào)用中的元素需要Key值。
key值在兄弟姐妹中唯一
數(shù)組中使用的key在其兄弟之間應該是唯一的值骇。但是莹菱,它們不需要是全局唯一的。當我們生成兩個不同的數(shù)組時吱瘩,我們可以使用相同的key:
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}
const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('root')
);
運行結(jié)果:
表單
用戶在表單填入的內(nèi)容道伟,屬于用戶跟組件的互動,所以不能用 this.props
讀取使碾。
/**表單 */
class NameForm extends Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSumbit = this.handleSumbit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSumbit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault(); // 阻止元素發(fā)生默認的行為(例如蜜徽,當點擊提交按鈕時阻止對表單的提交)
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
)
}
}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
上面代碼中,文本輸入框的值票摇,不能用 this.props.value
讀取拘鞋,而要定義一個 onChange
事件的回調(diào)函數(shù),通過 event.target.value
讀取用戶輸入的值矢门。textarea
元素盆色、select
元素、radio
元素都屬于這種情況颅和,更多介紹請參考官方文檔傅事。