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'))
复制代码