React.js
是一個幫助你構建頁面 UI
的庫。若是你熟悉 MVC
概念的話,那麼 React
的組件就至關於 MVC
裏面的 View
。若是你不熟悉也不要緊,你能夠簡單地理解爲,React.js
將幫助咱們將界面分紅了各個獨立的小塊,每個塊就是組件,這些組件之間能夠組合、嵌套,就成了咱們的頁面。css
一個組件的顯示形態和行爲有多是由某些數據決定的。而數據是可能發生改變的,這時候組件的顯示形態就會發生相應的改變。而 React.js
也提供了一種很是高效的方式幫助咱們作到了數據和組件顯示形態之間的同步。html
React.js 不是一個框架,它只是一個庫。它只提供 UI (view
)層面的解決方案。在實際的項目當中,它並不能解決咱們全部的問題,須要結合其它的庫,例如 Redux
、React-router
等來協助提供完整的解決方法。node
create-react-app
是來自於 Facebook
出品的零配置命令行工具,可以幫你自動建立基於Webpack+ES6
的最簡易的 React
項目模板。react
npm install -g create-react-app
create-react-app my-app
cd my-app/
npm start
複製代碼
執行完上述命令以後,你能夠直接打開 http://localhost:3000,便可以看到你 React APP
的運行效果:git
目錄結構:es6
my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── serviceWorker.js
複製代碼
一個簡單 Hello World
的例子:shell
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
複製代碼
JSX —— React.js
描述頁面 UI
的方式。看起來,JSX
有點像模板語言,其實它是由 React
內部實現的。瀏覽器中,看到的 JSX 內容轉換成 html
顯示了出來。npm
index.js
:json
import React from 'react'
import ReactDOM from 'react-dom'
const user = {
firstName: 'React',
lastName: 'Hello'
}
const formatName = user => `${user.firstName} ${user.lastName}`
const element = <h1>Hello, {formatName(user)}</h1>
ReactDOM.render(element, document.getElementById('root'))
複製代碼
推薦在 JSX 代碼的外面擴上一個小括號,這樣能夠防止 分號自動插入 的 bug
。數組
在編譯以後,JSX
其實會被轉化爲普通的 JavaScript
對象。那就能夠在 if
或者 for
語句裏使用 JSX
,將它賦值給變量,看成參數傳入,做爲返回值也能夠:
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
const user = {
firstName: 'E2E',
lastName: 'team'
}
const formatName = user => `${user.firstName} ${user.lastName}`
const getGreeting = (user) => {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
const element = <div>{getGreeting(user)}!</div>
ReactDOM.render(element, document.getElementById('root'))
複製代碼
若是 JSX
標籤是閉合式的,那麼你須要在結尾處用 />
, 就好像 XML/HTML 同樣:
const element = <img src={user.avatarUrl} />; 複製代碼
JSX 標籤一樣能夠相互嵌套:
const element = (
<div> <div>頭像</div> <h1>Hello, {formatName(user)}!</h1> </div>
)
複製代碼
注意:多行的 jsx 要用小括號包裹,裏面若是有多個 DOM
節點,也須要用一個 DOM
節點包裹起來,因此這裏加了最外面的 div
。
const element = <div tabIndex="0"></div>;
複製代碼
注意:因爲相對HTML
而言,JSX
更加相似於JavaScript
, React DOM
使用駝峯命名代替HTML
中的屬性名。
JavaScript
表達式爲值的屬性:const element = <img src={user.avatarUrl}></img>;
複製代碼
注意:使用了大括號包裹的 JavaScript
表達式時就不能再到外面套引號了,JSX 會將引號中的內容識別爲字符串而不是表達式。
render() {
const content = 'First · <i>Second</i>'
const element = <div>{content}</div>
return element
}
複製代碼
也就是在執行渲染前,React DOM
會默認將要顯示的內容中有任何的標籤或者腳本都會進行轉義編碼,按照字符串直接顯示出來。 這能夠避免應用被注入,能夠避免XSS
攻擊。因此能夠放心地在JSX
當中使用用戶輸入。
若是須要按照 html
顯示的內容,可使用 dangerouslySetInnerHTML
來實現。
const element = <div dangerouslySetInnerHTML={{ __html: content }} /> 複製代碼
Babel
轉譯器會把 JSX
轉換成一個名爲 React.createElement()
的方法調用。
下面兩種代碼的做用是徹底相同的:
const element = (
<h1 className="greeting"> Hello, world! </h1>
);
複製代碼
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
複製代碼
React.createElement()
這個方法首先會進行一些避免bug的檢查,以後會返回一個相似下面例子中的對象:
// 注意: 如下示例是簡化過的(不表明在 React 源碼中是這樣)
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
};
複製代碼
這樣的對象被稱爲 React 元素。它表明全部能夠在屏幕上看到的東西。React
經過讀取這些對象來構建 DOM
並保持數據內容一致。
元素( element
)是一個 React
應用的最小組成單元。
index.js
import React from 'react'
import ReactDOM from 'react-dom'
const element = <h1>Hello, world</h1>
ReactDOM.render(element, document.getElementById('root'))
複製代碼
這裏 element
就是一個元素, 元素描述了咱們在屏幕上會看到什麼。React
元素不是組件,組件由元素構成。
能夠經過 ReactDOM.render
把元素渲染到 DOM
中,id
爲 root
的這個節點在 index.html
中。
瀏覽器中,能夠看到 element
元素顯示到了頁面上。
Index.js
:
import React from 'react';
import ReactDOM from 'react-dom';
const tick = () => {
const element = (
<div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div>
)
ReactDOM.render(element, document.getElementById('root'))
}
setInterval(tick, 1000)
複製代碼
組件( components )可讓咱們把 UI 分割成獨立的能夠複用的片斷。概念上來說,組件相似於 JS 的函數,它接收任意的輸入(也就是 props ,屬性),返回 React 元素。
定義一個組件最簡單的方式是寫一個 JS 的函數:
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
const Welcome = props => {
return <h1>Hello, {props.name}</h1>
}
const element = <Welcome name="Sara" /> ReactDOM.render(element, document.getElementById('root')) 複製代碼
這個函數就是一個完整的 React 組件,由於它接收一個 props 對象做爲參數,返回一個 React 元素。這樣的組件叫作函數式組件。
另一個定義組件的方式就是使用 ES6 的 class:
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>
}
}
const element = <Welcome name="Sara" /> ReactDOM.render(element, document.getElementById('root')) 複製代碼
從 React 的角度,上面兩個組件是等價的。不過 class 式組件功能會多一些。
組件能夠在它的輸出中引用其它組件,這就可讓咱們用同一組件來抽象出任意層次的細節。在React應用中,按鈕、表單、對話框、整個屏幕的內容等,這些一般都被表示爲組件。
例如,咱們能夠建立一個App
組件,用來屢次渲染Welcome
組件:
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>
}
}
const App = () => {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
複製代碼
瀏覽器中,顯示了三個 Welcome
。
class
式組件要比函數式組件功能多,使用 state
就是隻能用在 class
式組件中的功能。
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div>
)
}
}
const element = <Clock /> ReactDOM.render(element, document.getElementById('root')) 複製代碼
Clock
是一個 class
式組件。裏面初始化了 state
值。而後 render
函數中,顯示出了這個 state
值。
每當 Clock
組件第一次加載到 DOM
中的時候,咱們都想生成定時器,這在 React
中被稱爲掛載
一樣,每當 Clock
生成的這個 DOM
被移除的時候,咱們也會想要清除定時器,這在 React
中被稱爲卸載
。
index.js
:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class Clock extends Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(this.tick, 1000)
}
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')) 複製代碼
組件初次渲染以後,會自動執行 componentDidMount
這個生命週期方法,這裏面咱們設置一個定時器,每秒鐘執行一下 tick
方法。這裏把定時器 id
賦值給了 this.timerID
。
組件被從 DOM
移除的時候,會自動執行 componentWillUnmount
,這裏面咱們須要清除一下定時器,釋放資源。
來定義關鍵的 tick
函數,裏面的關鍵動做就是更新 state
值。注意必定要用 this.setState
來更新。
瀏覽器中,能夠看到每秒鐘界面顯示時間都會更新。
React 元素的事件處理和 DOM元素的很類似。可是有一點語法上的不一樣:
index.js
:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class Toggle extends 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}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button>
)
}
}
ReactDOM.render(<Toggle />, document.getElementById('root')) 複製代碼
注意:類的方法默認是不會綁定 this
的。能夠將 handleClick
直接賦值爲一個 es6
箭頭函數,這樣的好處是裏面直接使用 this
而無需綁定。因爲 this.setState
的異步性,因此參數不能傳入對象,而要傳入一個函數,才能穩妥的基於以前的狀態來得到最新狀態值。
一般咱們會爲事件處理程序傳遞額外的參數。例如,如果 id
是你要刪除那一行的 id
:
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
class List extends React.Component {
deleteRow = id => {
console.log(id)
}
render() {
return <button onClick={() => this.deleteRow(2)}>Delete Row</button>
}
}
ReactDOM.render(<List />, document.getElementById('root')) 複製代碼
好比有一個列表,這裏封裝成 List 組件。裏面 deleteRow
須要接受行號,這裏就是 id ,才能知道要刪除哪一行的內容。
若是 deleteRow
中,還想要事件對象:
deleteRow = (id, e) => {
console.log(id)
}
render() {
return <button onClick={e => this.deleteRow(2, e)}>Delete Row</button>
}
複製代碼
ES6
參數中拿到 e
,把它做爲第二個參數傳遞給 deleteRow
便可。
在 React
中,你能夠建立不一樣的組件來封裝各類你須要的行爲。而後還能夠根據應用的狀態變化只渲染其中的一部分。
React
中的條件渲染和 JavaScript
中的一致,使用 JavaScript
操做符 if
或條件運算符來建立表示當前狀態的元素,而後讓 React
根據它們來更新 UI
。
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
const UserGreeting = () => <h1>Welcome back!</h1>
const GuestGreeting = () => <h1>Please sign up.</h1>
const Greeting = props => {
const isLoggedIn = props.isLoggedIn
if (isLoggedIn) {
return <UserGreeting />
}
return <GuestGreeting />
}
ReactDOM.render(<Greeting isLoggedIn={true} />, document.getElementById('root'))
複製代碼
先定義兩個函數式組件,一個是跟已經登錄的用戶打招呼,另外一個跟訪客打招呼。下面定義 Greeting
組件。隨着 isLoggedIn
的值的不一樣,會顯示出不一樣的內容。
瀏覽器中,當 isLoggedIn
設置爲 true
和 false
,會分別顯示不一樣的打招呼信息。
你可使用變量來儲存元素。它能夠幫助你有條件的渲染組件的一部分,而輸出的其餘部分不會更改。
index.js
:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
const UserGreeting = () => <h1>Welcome Back</h1>
const GuestGreeting = () => <h1>Please Sign Up</h1>
const LoginButton = props => <button onClick={props.onClick}>Login</button>
const LogoutButton = props => <button onClick={props.onClick}>Logout</button>
const Greeting = props => {
const { isLoggedIn } = props
if (isLoggedIn) {
return <UserGreeting />
}
return <GuestGreeting />
}
class LoginControl extends Component {
state = {
isLoggedIn: false
}
handleLoginClick = () => {
this.setState({
isLoggedIn: true
})
}
handleLogoutClick = () => {
this.setState({
isLoggedIn: false
})
}
render() {
const { isLoggedIn } = this.state
let button = null
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />
} else {
button = <LoginButton onClick={this.handleLoginClick} />
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
)
}
}
ReactDOM.render(<LoginControl />, document.getElementById('root'))
複製代碼
添加兩個按鈕組件進來,一個是登陸,一個是登出。建立一個 LoginControl
組件,初始化 isLoggedIn
,添加登陸和登出對應的處理函數,裏面對 isLoggedIn
狀態值進行了修改。
JavaScript
的邏輯與 &&
,它能夠方便地條件渲染一個元素。
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
const Mailbox = props => {
const { unreadMessages } = props
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') ) 複製代碼
定義 Mailbox
組件,屬性中拿到未讀郵件的數組,下面用 &&
號實現 if
的效果,若是未讀郵件數量大於 0,就顯示未讀郵件的數量;若是數量爲 0,那麼大括號裏面內容就求值爲 undefined
,也就是什麼都不會顯示了。
在下面的例子中,咱們用它來有條件的渲染一小段文本:
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div> The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in. </div>
);
}
複製代碼
一樣它也能夠用在較大的表達式中:
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
複製代碼
在極少數狀況下,你可能但願隱藏組件,即便它被其餘組件渲染。讓 render
方法返回 null
而不是它的渲染結果便可實現。
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
const 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 }
}
handleToggleClick = () => {
this.setState({
showWarning: !this.state.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') ); 複製代碼
先看下在 Javascript
中如何轉化列表:
咱們使用 map()
函數讓數組中的每一項翻倍,咱們獲得了一個新的數列doubled
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
複製代碼
在 React
中,把數組轉化爲數列元素的過程是類似的:
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
const messages = ['hello', 'hi', 'how are you']
const List = props => {
const { messages } = props
const list = messages.map(t => <li>{t}</li>)
return <ul>{list}</ul>
}
ReactDOM.render(<List messages={messages} />, document.getElementById('root'))
複製代碼
注:此時打開瀏覽器控制檯會有報錯信息:Warning: Each child in an array or iterator should have a unique "key" prop.
。緣由是每個列表條目都應該有一個獨一無二的 key
。
把數據的 id 做爲 key 是很是常見的作法:
index.js
:
import React from 'react'
import ReactDOM from 'react-dom'
const messages = [
{
id: 1,
text: 'React'
},
{
id: 2,
text: 'Re: React'
},
{
id: 3,
text: 'Re:Re: React'
}
]
const List = props => {
const { messages } = props
const list = messages.map(t => <li key={t.id}>{t.text}</li>)
return <ul>{list}</ul>
}
ReactDOM.render(<List messages={messages} />, document.getElementById('root'))
複製代碼
一個元素的 key
最好是這個元素在列表中擁有的一個獨一無二的字符串。一般,咱們使用來自數據的 id
做爲元素的key。
實際開發中的數據通常都是配有 id
的,將 id
做爲 key
是一個很好的作法。若是用數組 index
做爲 key 也是勉強能夠的,可是因爲 index
可能會隨着數組元素的增減發生變化,若是列表能夠從新排序,這會致使渲染變得很慢。
當用戶提交表單時,HTML
的默認行爲會使這個表單跳轉到一個新頁面。在 React
中亦是如此。
但大多數狀況下,咱們都會構造一個處理提交表單並可訪問用戶輸入表單數據的函數。實現這一點的標準方法是使用一種稱爲受控組件的技術。
<input>
或 <select>
都要綁定一個 change
事件,每當表單的狀態發生變化,都會被寫入組件的 state
中,這種組件在 React
中被稱爲受控組件。
index.js
:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class Form extends Component {
constructor(props) {
super(props);
this.state = { username: '' }
}
handleChange = e => {
this.setState({
username: e.target.value
})
}
handleSubmit = e => {
console.log(this.state.username)
e.preventDefault()
}
render() {
return (
<div> username: <input type="text" value={this.state.username} onChange={this.handleChange} /> <button onClick={this.handleSubmit}>提交</button> </div> ) } } ReactDOM.render(<Form />, document.getElementById('root')) 複製代碼
因爲 value
屬性是在咱們的表單元素上設置的,所以顯示的值將始終爲 React
數據源上this.state.value
的值。因爲每次按鍵都會觸發 handleChange
來更新當前 React
中的 state
,所展現的值也會隨着不一樣用戶的輸入而更新。
你有處理多個受控的 input
元素時,你能夠經過給每一個元素添加一個 name
屬性,來讓處理函數根據 event.target.name
的值來選擇作什麼。
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class Form extends Component {
constructor(props) {
super(props);
this.state = { username: '', email: '' }
}
handleChange = event => {
const { value, name } = event.target
this.setState({
[name]: value
})
}
handleSubmit = e => {
console.log(`${this.state.username} ${this.state.email}`)
e.preventDefault()
}
render() {
return (
<div>
Username:
<input
name="username"
type="text"
value={this.state.username}
onChange={this.handleChange}
/>
<br />
Email:
<input
name="email"
type="text"
value={this.state.email}
onChange={this.handleChange}
/>
<br />
<button onClick={this.handleSubmit}>提交</button>
</div>
)
}
}
ReactDOM.render(<Form />, document.getElementById('root'))
複製代碼