React的基本使用
1、腳手架安裝React項(xiàng)目
安裝 create-react-app 腳手架來(lái)創(chuàng)建 react 項(xiàng)目。
npm install -g create-react-app
create-react-app my-app
使用該腳手架創(chuàng)建的 react 項(xiàng)目默認(rèn)是不顯示配置文件的,可以使用 npm run eject 命令將配置文件顯示出來(lái),該命令是一個(gè)單向操作,即一旦運(yùn)行了,就不能再次將配置文件隱藏。
2、JSX語(yǔ)法
JSX是一種 JavaScript 的語(yǔ)法擴(kuò)展,react 用它來(lái)聲明 React 中的元素。你可以在 JSX 當(dāng)中任意地使用 JavaScript 表達(dá)式,不過(guò)在 JSX 當(dāng)中的表達(dá)式要包含在大括號(hào)里。
//JSX語(yǔ)法 const element = <h1>Hello, world!</h1>; function formatName(user) { return user.firstName + ' ' + user.lastName; } //在 JSX 中使用表達(dá)式 const element = ( <h1> Hello, {formatName(user)}! </h1> ); // JSX 其實(shí)就是一個(gè) JS 對(duì)象,你可以將它賦值給變量,或者當(dāng)作參數(shù)傳入、作為返回值都可以 function getGreeting(user) { if (user) { return <h1>Hello, {formatName(user)}!</h1>; } return <h1>Hello, Stranger.</h1>; }
在代碼編譯過(guò)后你可以看到 JSX 會(huì)被轉(zhuǎn)化為普通的 JavaScript 對(duì)象,所以實(shí)際上,JSX 語(yǔ)法返回的是一個(gè) JS 對(duì)象,這個(gè)對(duì)象也被稱(chēng)為 React 元素。
在 JSX 中我們可以放心地使用用戶(hù)輸入,因?yàn)镽eact DOM 在渲染之前默認(rèn)會(huì)過(guò)濾掉所有傳入的值,它可以確保你的應(yīng)用不會(huì)被注入攻擊。所有的內(nèi)容在渲染之前都被轉(zhuǎn)換成了字符串。這樣可以有效地防止 XSS(跨站腳本) 攻擊。
const title = response.input; // 直接使用是安全的: const element = <h1>{title}</h1>;
2.1、JSX 的屬性
在 JSX 中,可以使用引號(hào)來(lái)定義以字符串為值的屬性,也可以使用大括號(hào)來(lái)定義以 JavaScript 表達(dá)式為值的屬性
const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;
使用了大括號(hào)包裹的 JavaScript 表達(dá)式時(shí)就不要再到外面套引號(hào)了,因?yàn)镴SX 會(huì)將引號(hào)當(dāng)中的內(nèi)容識(shí)別為字符串而不是表達(dá)式。
2.2、更新元素渲染
JSX 語(yǔ)法返回的是一個(gè) JS 對(duì)象,這個(gè)對(duì)象也被稱(chēng)為 React 元素,元素是構(gòu)成 React 應(yīng)用的最小單位,在頁(yè)面上進(jìn)行渲染。
要將React元素渲染到根DOM節(jié)點(diǎn)中,我們通過(guò)把它們都傳遞給 ReactDOM.render() 的方法來(lái)將其渲染到頁(yè)面上
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
React 元素都是不可變的。當(dāng)元素被創(chuàng)建之后,我們無(wú)法改變其內(nèi)容或?qū)傩?。如果使用上面的方法,即使用元素?lái)渲染頁(yè)面,更新界面的唯一辦法是創(chuàng)建一個(gè)新的元素,然后將它傳入 ReactDOM.render() 方法替換之前的元素。當(dāng)然,我們可以通過(guò)使用有狀態(tài)組件(類(lèi)定義組件)來(lái)避免這種落后的方法。
3、組件
3.1、定義組件
組件可以接收任意的輸入值(稱(chēng)之為“props”),并返回一個(gè) React 元素,即 JS 對(duì)象。
函數(shù)定義組件
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
ES6 類(lèi)定義組件
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
注意:組件的返回值只能有一個(gè)根元素。組件名稱(chēng)必須以大寫(xiě)字母開(kāi)頭。例如,<div /> 表示一個(gè)DOM標(biāo)簽,但 <Welcome /> 表示一個(gè)組件,并且在使用該組件時(shí)你必須定義或引入它。
3.2、組件接收傳入值(props)
React 元素可以是DOM標(biāo)簽,也可以是用戶(hù)自定義的組件,當(dāng) React 元素是用戶(hù)自定義的組件,它會(huì)將 JSX 屬性作為單個(gè)對(duì)象傳遞給該組件,這個(gè)對(duì)象稱(chēng)之為“props”。
const element = <div />; //用戶(hù)自定義的組件 const element = <Welcome name="Sara" />;
在組件中,不允許修改 props 的值,只允許讀取。
4、類(lèi)組件
使用元素渲染或者是函數(shù)定義組件渲染來(lái)更新頁(yè)面非常不方便,所以在一個(gè) react 項(xiàng)目中我們應(yīng)該使用類(lèi)組件來(lái)渲染頁(yè)面。使用類(lèi)我們就可以使用它的一些特性,例如局部狀態(tài) state、生命周期鉤子等,這大大方便了我們的開(kāi)發(fā)。
類(lèi)組件代碼示例:
class Clock extends React.Component { constructor(props) { super(props); //類(lèi)組件應(yīng)始終使用 props 調(diào)用基礎(chǔ)構(gòu)造函數(shù)。 this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
4.1、組件生命周期
官網(wǎng)的組件生命周期圖:

我們可以在生命周期鉤子上定義一些函數(shù)來(lái)進(jìn)行一些操作,如下實(shí)現(xiàn)了一個(gè)每秒鐘修改一次狀態(tài)的效果:
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } //組件掛載后運(yùn)行 componentDidMount() { //如果數(shù)據(jù)不渲染在頁(yè)面上,你可以直接往類(lèi)上添加字段,不需要添加到state上 this.timerID = setInterval( () => this.tick(), 1000 ); } //組件卸載前運(yùn)行 componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setState({ date: new Date() }); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );
4.2、局部狀態(tài)(state、setState)
局部狀態(tài) state 用來(lái)維護(hù)組件內(nèi)部數(shù)據(jù),可以通過(guò) this.state.propertyName 來(lái)獲取數(shù)據(jù)。構(gòu)造函數(shù)是唯一能夠初始化 this.state 的地方。
組件可以將它的狀態(tài)作為屬性傳遞給其子組件,但是除了擁有并設(shè)置它的組件外,其它組件不可訪問(wèn)另一個(gè)組件的狀態(tài),這就是為什么狀態(tài)通常被稱(chēng)為局部或封裝。
在更改局部狀態(tài)時(shí),不要直接修改,而應(yīng)該使用 setState 方法,否則 react 不會(huì)重新渲染組件。setState 方法只是將你返回的對(duì)象合并到局部狀態(tài) state 中,并且是淺合并,即返回的對(duì)象中提及到的屬性會(huì)被完全替換,沒(méi)有涉及到的屬性不受影響。
//Wrong 不要直接修改 //this.state.comment = 'Hello'; // 應(yīng)當(dāng)使用 setState() 方法 this.setState({comment: 'Hello'});
在利用當(dāng)前狀態(tài)和 props 的值來(lái)修改下一狀態(tài)時(shí),應(yīng)該用一個(gè)函數(shù)來(lái)作為 setState 的參數(shù)。否則如果直接修改的話,有可能更改會(huì)沒(méi)有效果,因?yàn)檫@兩種值是異步更新的。
// Wrong 不應(yīng)該直接修改 //this.setState({ // counter: this.state.counter + this.props.increment, //}); //setState() 應(yīng)該接受一個(gè)函數(shù)而不是一個(gè)對(duì)象。 該函數(shù)將接收先前的狀態(tài)作為第一個(gè)參數(shù),將props做為第二個(gè)參數(shù): this.setState((prevState, props) => ({ counter: prevState.counter + props.increment })); //或者使用常規(guī)函數(shù) this.setState(function(prevState, props) { return { counter: prevState.counter + props.increment }; });
4.3、父子組件傳值及事件調(diào)用
react 中父組件可以將數(shù)據(jù)作為子組件的屬性進(jìn)行傳值,子組件通過(guò) props 屬性接收值。
父組件可以監(jiān)聽(tīng)子組件中的某個(gè)事件,子組件直接在內(nèi)部觸發(fā) props 事件即可以觸發(fā)父組件監(jiān)聽(tīng)的事件,由此父組件可以進(jìn)行響應(yīng)。
//子組件 class Child extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange(e) { //子組件觸發(fā) props 事件 this.props.onDataChange(e.target.value); } render() { const val = this.props.val; return ( <input value={val} onChange={this.handleChange} /> ); } }
//父組件 class Calculator extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = {name: 'wen'}; } //父組件將 onDataChange 傳遞給子組件即監(jiān)聽(tīng)了該事件,當(dāng)子組件觸發(fā)該事件時(shí),父組件觸發(fā) handleChange 事件 handleChange(name) { this.setState({name}); } render() { return ( <div> <Child val={this.state.name} onDataChange={this.handleChange} /> </div> ); } }
5、事件處理
React 元素的事件函數(shù)命名采用駝峰式寫(xiě)法,這點(diǎn)跟普通 DOM 元素調(diào)用函數(shù)不太一樣。
<!- 普通 DOM 元素 --> <button onclick="activateLasers()"> Activate Lasers </button> <!- react 語(yǔ)法 --> <button onClick={activateLasers}> Activate Lasers </button>
在 React 中另一個(gè)不同是你不能使用返回 false 的方式阻止默認(rèn)行為。你必須明確的使用 preventDefault。
<!- 普通 DOM 元素 --> <a href="#" onclick="console.log('The link was clicked.'); return false"> Click me </a> <!- react 語(yǔ)法 --> function handleClick(e) { e.preventDefault(); console.log('阻止默認(rèn)行為'); } return ( <a href="#" onClick={handleClick}> Click me </a> );
事件處理的 this 指針問(wèn)題及如何綁定 this 可以參考:http://www.rzrgm.cn/wenxuehai/p/11378229.html
5.1、給事件函數(shù)傳遞參數(shù)
為事件處理函數(shù)傳遞額外的參數(shù),下面兩種方式是等價(jià)的:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
箭頭函數(shù)傳遞參數(shù)時(shí),事件對(duì)象必須顯式的進(jìn)行傳遞,但是通過(guò) bind 的方式,事件對(duì)象及其它的一些參數(shù)將會(huì)被隱式的進(jìn)行傳遞。
通過(guò) bind 方式向監(jiān)聽(tīng)函數(shù)傳參,在類(lèi)組件中定義的監(jiān)聽(tīng)函數(shù),事件對(duì)象 e要排在所傳遞參數(shù)的后面
preventPop(name, e){ //事件對(duì)象e要放在最后
e.preventDefault();
alert(name);
}
<a href="" onClick={this.preventPop.bind(this,'aaa')}>Click</a>
6、條件渲染
在 react 中,我們可以使用 if 語(yǔ)句、&& 運(yùn)算符、三元運(yùn)算符來(lái)實(shí)現(xiàn)條件渲染,主要就是通過(guò)運(yùn)算符來(lái)控制元素是否顯示。
//if 語(yǔ)句控制是否渲染 render() { let button = null; if (true) { button = <LogoutButton onClick={this.handleLogoutClick} />; } else { button = <LoginButton onClick={this.handleLoginClick} />; } return ( <div>{button}</div> ); } // &&運(yùn)算符控制是否渲染,在react中,大括號(hào){}內(nèi)如果是false,會(huì)忽略并跳過(guò),并不會(huì)渲染出來(lái) <div> <h1>Hello!</h1> {arr.length > 0 && <h2>aaa</h2> } </div> //三元運(yùn)算符 <div> {isLoggedIn ? ( <LogoutButton/> ) : ( <LoginButton /> )} </div>
如果不想讓組件渲染,可以讓 render 方法返回 null,組件的render方法返回null并不會(huì)影響該組件生命周期方法的回調(diào)。
7、列表渲染
在 react 中,可以使用 map 遍歷方法來(lái)實(shí)現(xiàn)列表渲染。
//可以生成一個(gè) JSX 元素,然后將該 JSX 元素插入渲染 function NumberList(props) { const numbers = [1,2,3,5,6]; const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> ); return ( <ul>{listItems}</ul> ); } //或者直接在大括號(hào)中寫(xiě)遍歷表達(dá)式,因?yàn)镴SX允許在大括號(hào)中嵌入任何表達(dá)式 function NumberList(props) { const numbers = props.numbers; return ( <ul> {numbers.map((number) => <ListItem key={number.toString()} value={number} /> )} </ul> ); }
7.1、列表渲染中的 key
react 在使用列表渲染時(shí),必須給每個(gè)列表元素分配一個(gè) key,否則會(huì)出現(xiàn)一個(gè)警告 a key should be provided for list items。
列表渲染時(shí),key只需在兄弟元素之間唯一即可。react 在進(jìn)行列表渲染時(shí),數(shù)組元素中使用的 key 在其兄弟元素之間應(yīng)該是獨(dú)一無(wú)二的。然而,它們并不需要是全局唯一的,即如果不是同一列表兄弟元素的話,它們的 key 值是可以重復(fù)的。當(dāng)我們?cè)谕粋€(gè)頁(yè)面中先后進(jìn)行兩次列表渲染時(shí),我們可以使用相同的鍵。
//同一組件中,不同列表元素,key值可以重復(fù) 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') );
8、表單元素
8.1、受控組件
使用受控組件可以將用戶(hù)輸入的值保存在組件的狀態(tài)屬性中,并且能控制用戶(hù)輸入時(shí)所發(fā)生的變化。
class NameForm extends React.Component { constructor(props) { super(props); this.state = {value: ''}; this.handleChange = this.handleChange.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } //必須要主動(dòng)綁定 onChange 事件,并且修改 value 值,此時(shí) value的值才會(huì)改變。react并沒(méi)有像Vue那樣有v-model的功能 render() { return ( <input type="text" value={this.state.value} onChange={this.handleChange} /> ); } }
textarea、select標(biāo)簽寫(xiě)法都類(lèi)似,可參考:https://www.reactjscn.com/docs/forms.html
當(dāng)有多個(gè)受控元素時(shí),可以給每個(gè)元素都添加一個(gè) name 屬性,處理函數(shù)就可以根據(jù) name 屬性的值來(lái)進(jìn)行相應(yīng)操作,由此可以避免重復(fù)寫(xiě)處理函數(shù)。
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; //可以這樣設(shè)置狀態(tài) this.setState({ [name]: value }); } render() { return ( <input name="isGoing" type="checkbox" checked={this.state.isGoing} onChange={this.handleInputChange} /> <input name="numberOfGuests" type="number" value={this.state.numberOfGuests} onChange={this.handleInputChange} /> ); } }
8.2、非受控組件
如果你覺(jué)得需要為每個(gè)表單元素綁定一個(gè)處理函數(shù)比較麻煩,你可以嘗試使用非受控組件。非受控組件將真實(shí)數(shù)據(jù)保存在 DOM 中,我們可以使用 ref 從 DOM 中獲取表單值。
使用非受控組件可以減小代碼量,但并不推薦使用,推薦還是使用受控組件。
class NameForm extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(event) { alert(this.input.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <input type="text" ref={(input) => this.input = input} /> <input type="submit" value="Submit" /> </form> ); } }
非受控組件還可以指定元素初始值,使用 defaultValue
<input defaultValue="Bob" type="text" ref={(input) => this.input = input} />
9、組合(插槽)
在 react 中,可以通過(guò)使用 props.children 來(lái)實(shí)現(xiàn)類(lèi)似于 Vue 中的插槽的功能。
//子組件 function FancyBorder(props) { return ( <div className={'FancyBorder FancyBorder-' + props.color}> {props.children} </div> ); } //父組件 function WelcomeDialog() { return ( <FancyBorder> <h1>Welcome</h1> </FancyBorder> ); }
具名插槽:
//子組件 function SplitPane(props) { return ( <div className="SplitPane"> <div> {props.left} </div> <div> {props.right} </div> </div> ); } //父組件 function App() { return ( <SplitPane left={ <Contacts /> } right={ <Chat /> } /> ); }
上面的 <Contacts /> 和 <Chat /> 這樣的 React 元素其實(shí)本質(zhì)上都是對(duì)象,所以你可以像任何其他元素一樣傳遞它們。使用具名插槽其實(shí)就是通過(guò)屬性來(lái)傳遞數(shù)據(jù),不過(guò)此時(shí)的數(shù)據(jù)是一個(gè) react 元素而已。

浙公網(wǎng)安備 33010602011771號(hào)