React简介
React是用于构建Web和原生交互界面的开源JS库。
React特点
-
声明式设计,为应用的每一个状态设计简洁的视图,当数据改变的时候,React能有效、正确的更新、渲染组件。
-
高效:React采用虚拟DOM,极大的提升了UI更新和渲染的效率;
-
灵活:React允许结合其它框架或者库一起使用;
-
JSX:JSX是JS语法的扩展,能很好的描述UI应该呈现出的应有的交互的本质形式;
-
单向响应式数据流:React采用了单向响应的数据流,使得组件状态清晰,模块化更易于维护和开发。
小试牛刀
使用create-react-app工具,创建React环境:
import React from 'react' // 很新库
import ReactDOM from 'react-dom' // 与DOM相关的功能
function SayHello(props) {
return <h1>say hello to you</h1>
}
// 将SayHello组件插入到id为root的标签中
ReactDOM.render(
<SayHello />,
document.getElementById('root')
)
启动项目,打开localhost:3000,即可看到页面上的h1标签样式展示的say hello to you。
React安装
React可以直接下载安装(官网:zh-hans.reactjs.org/)基础环境:Node。
- 查看Node版本以及npm,需要node >= 6 和 npm >= 5.2(建议使用cnpm)。
node -v
npm -v
- 全局安装React:
create-react-app,通过该命令,无需配置即可快速构建React环境(基于webpack+ES6).
npm install create-react-app -g
// cnpm安装
npm install -g cnpm --registry=https://registry.npm.taobao.org
npm config set registry https://registry.npm.taobao.org
npm install create-react-app -g
- 创建项目
// create-react-app 创建命令
// my-app 项目名
create-react-app my-app
// 进入项目
cd my-app
npm start
React元素渲染
React元素都是不可变的:当元素被创建后,无法改变其内容和属性。更新办法:创建一个新的元素,将它传入ReactDOM.render()中:
<div id="example"></div>
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('example')
);
}
setInterval(tick, 1);
运行结果如下:
注:React 只会更新必要的部分
React DOM 首先会比较元素内容先后的不同,在渲染过程中只会更新改变了的部分。
JSX语法
JSX:类XML的JS语法扩展(语法糖)。元素是构成React应用的最小单位,JSX就是用来声明其中的元素的。
特点
- 执行快,因为它在编译为JS代码后进行了优化;
- 类型安全,在编译过程中能快速发现错误;
- 快速:语义化,更直观,可读性更高;
使用
- 属性:className替换class,htmlFor替换for属性;
- JSX创建DOM,所有的节点,必须有唯一的根元素进行包裹;
- JSX中可直接使用JS代码,通过
{}包含即可; - JSX中只能出现表达式,不能出现语句;
- 注释语法:
{/* 中间是注释的内容 */}
具体使用参考文章:10分钟快速掌握React中的JSX:
React组件
- 使用函数定义组件或者使用ES6的
class来定义一个组件(大写字母开头,组件类只能包含一个根节点标签)
// 方法定义
function MyComponent({name, age}) {
return <div>hello, my name is {name} and i am a {age} years old.</div>
}
// class定义
class MyComponent1 extends React.Component {
render() {
return <div>hello, my name is {name} and i am a {age} years old.</div>
}
}
// 使用
const element = <MyComponent name='Jack' age=18 />
React.render(
element,
document.getElemementById('root')
)
组件API
- 状态
setState(object nextState[, callback])setState()会合并更新与当前的状态,并不会立即改变this.state,而是创建一个即将处理的state。setState()并不一定是同步的,为了提升性能React会批量执行state和DOM渲染;setState()总是会触发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑;
replaceState(object nextState[, callback])replaceState()仅保留nextState中的状态。
- 属性
setProps(object nextProps[, callback])replaceProps(object nextProps[, callback])
- 强制更新:
forceUpdate([callback])callback会在组件render()方法调用后调用- 避免使用
- 获取DOM节点:
findDOMNode() - 判断组件挂载状态:
isMounted():判断组件是否已挂载,保证异步场景下设置状态不出错。
组件的生命周期
- Mounting(挂载):已插入真实 DOM
constructor(): 在 React 组件挂载之前,会调用它的构造函数。getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。render(): render() 方法是 class 组件中唯一必须实现的方法。componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用。
- Updating(更新):正在被重新渲染
getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。shouldComponentUpdate():当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。render(): render() 方法是 class 组件中唯一必须实现的方法。getSnapshotBeforeUpdate(): 在最近一次渲染输出(提交到 DOM 节点)之前调用。componentDidUpdate(): 在更新后会被立即调用。
- Unmounting(卸载):已移出真实 DOM
componentWillUnmount(): 在组件卸载及销毁之前直接调用。
React Ajax
React最贱的数据可以通过componentDidMount方法中的Ajax获取,当从服务端获取数据时可以将数据存储在state中,在用this.setState方法重新渲染UI。
当使用异步加载数据时,在组件卸载前使用componentWillUnmount来取消未完成的请求。
class UserGist extends React.Component {
constructor(props) {
super(props);
this.state = {username: '', lastGistUrl: ''};
}
componentDidMount() {
this.serverRequest = $.get(this.props.source, function (result) {
var lastGist = result[0];
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
}
componentWillUnmount() {
this.serverRequest.abort();
}
render() {
return (
<div>
{this.state.username} 用户最新的 Gist 共享地址:
<a href={this.state.lastGistUrl}>{this.state.lastGistUrl}</a>
</div>
);
}
}
ReactDOM.render(
<UserGist source="https://api.github.com/users/octocat/gists" />,
document.getElementById('example')
);
小试牛刀
在 Hello 组件加载以后,通过 componentDidMount 方法设置一个定时器,每隔100毫秒重新设置组件的透明度,并重新渲染:
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {opacity: 1.0};
}
componentDidMount() {
this.timer = setInterval(function () {
var opacity = this.state.opacity;
opacity -= .05;
if (opacity < 0.1) {
opacity = 1.0;
}
this.setState({
opacity: opacity
});
}.bind(this), 100);
}
render () {
return (
<div style={{opacity: this.state.opacity}}>
Hello {this.props.name}
</div>
);
}
}
ReactDOM.render(
<Hello name="world"/>,
document.body
);
React表单事件
表单
在React中,可变的状态通常保存在组件的状态属性中,并且只能用setState()进行更新。因此,需要通过一定的事件处理完成表单数据同步更新。
class HelloMessage extends React.Component{
constructor(props) {
// 调用父类Component的props的构造方法
super(props);
this.state = {name:'LeaT'}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({name: event.target.value})
}
render() {
let value = this.state.name
return <div>
<input value={value} onChange={this.handleChange} type="text" />
<h4>Say hello to {value}</h4>
</div>
}
}
ReactDOM.render(
<HelloMessage />
document.getElementById('root')
)
事件处理
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Hello Runoob!'};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: 'hello'})
}
render() {
var value = this.state.value;
return <div>
<button onClick={this.handleChange}>点我</button>
<h4>{value}</h4>
</div>;
}
}
ReactDOM.render(
<HelloMessage />,
document.getElementById('example')
);
React state(状态)
React状态反应出来其实就是组件是一个状态机。通过与用户的交互实现不同状态修改、更新UI,保持用户界面与数据的一致性。
单向数据流
组件的数据是单向数据流:任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或UI只能影响树中甲方的组件。
小试牛刀
function FormattedDate(props) {
return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
}
class Clock extends React.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>
<FormattedDate date={this.state.date} />
</div>
);
}
}
function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('example'));
React props
props相对于state是不可变的。通常,子组件就是通过props来传递数据。
class HelloMessage extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
HelloMessage.defaultProps = {
name: 'Runoob'
};
const element = <HelloMessage/>;
ReactDOM.render(
element,
document.getElementById('example')
);
父子组件参数传递
class WebSite extends React.Component {
constructor() {
super();
this.state = {
name: "菜鸟教程",
site: "https://www.runoob.com"
}
}
render() {
return (
<div>
<Name name={this.state.name} />
<Link site={this.state.site} />
</div>
);
}
}
class Name extends React.Component {
render() {
return (
<h1>{this.props.name}</h1>
);
}
}
class Link extends React.Component {
render() {
return (
<a href={this.props.site}>
{this.props.site}
</a>
);
}
}
ReactDOM.render(
<WebSite />,
document.getElementById('example')
);
propTypes(prop验证)
propTypes主要用于保证应用组件的正确使用,验证传参的数据格式和有效性。
验证器说明:
MyComponent.propTypes = {
// 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
// 可以被渲染的对象 numbers, strings, elements 或 array
optionalNode: React.PropTypes.node,
// React 元素
optionalElement: React.PropTypes.element,
// 用 JS 的 instanceof 操作符声明 prop 为类的实例。
optionalMessage: React.PropTypes.instanceOf(Message),
// 用 enum 来限制 prop 只接受指定的值。
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
// 可以是多个对象类型中的一个
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// 指定类型组成的数组
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
// 指定类型的属性构成的对象
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
// 特定 shape 参数的对象
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// 任意类型加上 `isRequired` 来使 prop 不可空。
requiredFunc: React.PropTypes.func.isRequired,
// 不可空的任意类型
requiredAny: React.PropTypes.any.isRequired,
// 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
}
}
React事件处理
- 驼峰式写法:
onClick= - JSX语法
{}包含函数名:onClick={activeLasers} - 阻止默认行为,需要使用preventDefault,不可使用false的方式阻止默认行为。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
类的方法默认是不会绑定this的。需手动绑定,在回调中才可使用
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('example')
);
React条件渲染
&&xx?xxx:xxxx- 阻止条件渲染:
return null即可,不会影响组件生命周期方法的回调。
React中的列表和key
React中的数组可直接使用,渲染时会平铺每一个元素。
function ListItem(props) {
return <li>{props.value}</li>;
}
使用JSX
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
key
React中的key主要用于DOM更新时,虚拟DOM的diff算法中使用,一个元素的key应该在列表以及兄弟元素中拥有一个独一无二的特性,全局唯一。
React refs
React允许在任何组件和元素上添加ref属性,通过this.refs.xx获取指定xx属性的组件的引用。
class MyComponent extends React.Component {
handleClick() {
// 使用原生的 DOM API 获取焦点
this.refs.myInput.focus();
}
render() {
// 当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
return (
<div>
<input type="text" ref="myInput" />
<input
type="button"
value="点我输入框获取焦点"
onClick={this.handleClick.bind(this)}
/>
</div>
);
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('example')
);