React本质上是一个”状态机”,可以帮助开发者管理复杂的随着时间而变化的状态。React只关心两件事:
1.更新DOM
2.响应事件
在每次状态改变时,使用JavaScript重新渲染整个页面会异常慢,这应该归咎于读取和更新DOM的性能问题。React运用一个虚拟的DOM实现了一个非常强大的渲染系统,在React中对DOM只更新不读取。
React以渲染函数为基础,这些函数读入当前的状态,将其转换为目标页面上的一个虚拟表现。只要React被告知状态有变化(更新组件的内部状态会触发组件重绘),它就会重新运行这些函数,计算出页面的一个新的虚拟表现,接着自动地把结果转换成必要的DOM更新来反映新的表现(React使用了非常高效的算法,计算出虚拟页面当前版本和新版本间的差异,基于这些差异对DOM进行必要的最少更新)。
React赢就赢在最小化了重绘,并避免了不必要的DOM操作。
ReactDOM.render
ReactDOM.render 是 React 的最基本方法(渲染函数),用于将模板转为 HTML 语言,并插入指定的 DOM 节点。
ReactDOM.render(
<h1>Hello, React!</h1>,
document.getElementById('example')
);
上面代码将一个 h1 标题,插入 example 节点
render方法需注意的几点:
1.只能通过this.props和this.state访问数据
2.可以返回null、false或者任何React组件
3.只能出现一个顶级组件(不能返回一组元素)
4.必须纯净,意味着不能改变组件的状态或者修改DOM的输出。
render方法返回的结果不是真正的DOM,而是一个虚拟的表现,React随后会把它和 真是的DOM(内存中的DOM)做对比,来判断是否有必要。
React JSX
React 使用 JSX 来替代常规的 JavaScript。JSX即JavaScript XML,是一种在React组件内部构建标签的类XML语法。
JSX的优点:
- 允许使用熟悉的语法来定义HTML元素树
- 提供更加语义化且易懂的标签(将传统的HTML标签封装成React组件),我们就可以像使用HTML标签一样使用这个组件。
- 程序结构更容易被直观化(在函数作用域内,使用JSX语法的代码与原生JavaScript相比,其标签的意图变得更加直观,可读性也更高)。
- 抽象了React Element的创建过程
- 可以随时掌控HTML标签以及生成这些标签的代码
- 是原生的JavaScript
使用动态值
JSX将两个花括号之间的内容{…}渲染为动态值。花括号指明了一个JavaScript上下文环境–你在花括号中放入的任何东西都会被进行求值,得到的结果被渲染为标签中的若干节点。
JavaScript 表达式
在 JSX 中使用 JavaScript 表达式。表达式要写在花括号 {} 中。实例如下:
ReactDOM.render(
<div>
<h1>{1+1}</h1>
</div>
,
document.getElementById('example')
);
在 JSX 中不能使用 if-else 语句,但可以使用三元运算表达式来替代。示例如下:
ReactDOM.render(
<div>
<h1>{i == 1 ? 'True' : 'False'}</h1>
</div>
,
document.getElementById('example')
);
样式
React 推荐使用内联样式,React 会在指定元素数字后自动添加 px 。示例如下:
var myStyle = {
fontSize: 30,
color: '#FF0000'
};
ReactDOM.render(
<h1 style = {myStyle}>Hello World</h1>,
document.getElementById('example')
);
注释
注释需要写在花括号中,实例如下:
ReactDOM.render(
<div>
<h1>React学习</h1>
{/*注释...*/}
</div>,
document.getElementById('example')
);
数组
JSX 允许在模板中插入数组,数组会自动展开所有成员:
var arr = [
<h1>Hello React</h1>,
<h2>Hello React Native</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
非DOM属性
key
key是一个可选的唯一标识符。在程序运行的过程中,一个组件可能会在组件树中调整位置,比如当用户在进行搜索操作时,或者当一个列表中的物品被增加、删除时。当这些情况发生时,组件可能并不需要被销毁并重新创建。
通过给组件设置一个独一无二的键,并确保它在一个渲染周期中保持一致,使得React能够更智能地决定应该重用一个组件,还是销毁并重新创建一个组件,进而提升渲染性能。当两个已经存在在于DOM中的组件交换位置时,React能够匹配对应的键并进行相应的移动,且不需要完全重新渲染DOM。
ref
ref允许父组件在render方法之外保持对子组件的一个引用。
在JSX中,你可以通过在属性中设置期望的引用名来定义一个引用。
render:function(){
return <div>
<input ref="myInput" />
</div>;
}
这样一来,我们就可以在组件中的任何地方使用this.refs.myInput获取这个引用了。通过引用获取到的这个对象被称为支持实例。它并不是真正的DOM,而是React在需要时用来创建DOM的一个描述对象。可以使用this.refs.myInput.getDOMNode()访问真实的DOM节点。
HTML 标签 vs React 组件
React 可以渲染 HTML 标签 (strings) 或 React 组件 (classes)。
- 要渲染 HTML 标签,只需在 JSX 里使用小写字母的标签名。
var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));
- 要渲染 React 组件,只需创建一个大写字母开头的本地变量。
var MyComponent = React.createClass({/*...*/});
var myElement = <MyComponent someProperty={true} />;
ReactDOM.render(myElement, document.getElementById('example'));
React 的 JSX 使用大、小写的约定来区分本地组件的类和 HTML 标签。
注意:
由于 JSX 就是 JavaScript,一些标识符像 class 和 for 不建议作为 XML 属性名。作为替代,React DOM 使用 className 和 htmlFor 来做对应的属性。
React 组件
在传统HTML中,元素是构成页面的基础单元,但在React中,构成页面的基础单元是React 组件。可以把React 组件理解成混入了Javascript表达能力的HTML元素。实际上写React 代码主要就是构建组件,就像编写HTML文档时使用元素一样。
Demo:
var HelloMessage = React.createClass({
render: function() {
return <h1>Hello {this.props.name}</h1>;
}
});
ReactDOM.render(
<HelloMessage name="React" />,
document.getElementById('example')
);
这里使用React.createClass 方法用于生成一个组件类 HelloMessage。
<HelloMessage />
实例组件类并输出信息。
如果需要向组件传递参数,可以使用 this.props 对象
注意:
- 原生 HTML 元素名以小写字母开头,而自定义的 React 类名以大写字母开头,比如 HelloMessage 不能写成 helloMessage。除此之外还需要注意组件类只能包含一个顶层标签,否则也会报错。
- 在添加属性时, class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。
React State(状态)
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
以下Demo中创建了 LikeButton 组件,getInitialState 方法用于定义初始状态,也就是一个对象,这个对象可以通过 this.state 属性读取。当用户点击组件,导致状态变化,this.setState 方法就修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件。
注:this.setState 方法仅仅是把传入的对象合并到已有的state对象。永远不要尝试通过setState或者replaceState以外的方式去修改state对象,因为会导致无法通知React是否需要重新渲染组件。
Demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>react state</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="../build/react.js"></script>
<script type="text/javascript" src="../build/react-dom.js"></script>
<script type="text/javascript" src="../build/browser.min.js"></script>
<script type="text/babel">
var LikeButton = React.createClass({
getInitialState:function(){
return {liked:true};
},
handleClick:function(event){
this.setState({liked:!this.state.liked});
},
render:function(){
var text = this.state.liked ? '喜欢':'不喜欢';
return (
<p onClick = {this.handleClick}>You {text} this(点击切换状态)</p>
);
}
});
ReactDOM.render(
<LikeButton/>,
document.getElementById("example")
);
</script>
</body>
</html>
从用户输入到更新用户界面:
1.在React组件上绑定事件处理器
2.在事件处理器当中更新组件的内部状态。组件状态的更新会触发重绘。
3.实现组件的render函数用来渲染this.state的数据
当组件里的按钮被点击时,触发 handleClick 函数:
- liked 状态被改变
- React 重新渲染虚拟 DOM
- 新老虚拟 DOM 进行对比
- React 识别出变化的部分并渲染到浏览器(只更新变化的部分)
React Props
state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。
Demo:
这个小例子组合使用了 state 和 props 。在父组件中设置了 state, 并通过在子组件上使用 props 将其传递到子组件上。在 render 函数中, 我们设置 name 和 site 来获取父组件传递过来的数据。子组件与其父组件通信的最简单方式就是使用属性(props)。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>state和props</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="../build/react.js"></script>
<script type="text/javascript" src="../build/react-dom.js"></script>
<script type="text/javascript" src="../build/browser.min.js"></script>
<script type="text/babel">
var Websites = React.createClass({
getInitialState:function(){
return {
name:"百度一下",
site:"http://www.baidu.com"
};
},
render:function(){
return (
<div>
<Name name = {this.state.name}/>
<Link site = {this.state.site}/>
</div>
);
}
});
var Name = React.createClass({
render:function(){
return (
<h1>{this.props.name}</h1>
);
}
});
var Link =React.createClass({
render:function(){
return(
<a href = {this.props.site}>{this.props.site}</a>
);
}
});
ReactDOM.render(
<Websites />,
document.getElementById("example")
);
</script>
</body>
</html>
React 组件生命周期
组件的生命周期可分成三个状态:
- Mounting:已插入真实 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真实 DOM
React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数:
- componentWillMount() 在渲染前调用,在客户端也在服务端。
- componentDidMount() 在第一次渲染后调用,只在客户端。
- componentWillUpdate() 在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用
- componentDidUpdate() 在组件完成更新后立即调用。在初始化时不会被调用。
- componentWillUnmount() 在组件从 DOM 中移除的时候立刻被调用。
React 的两种特殊状态的处理函数:
- componentWillReceiveProps():已加载组件收到新的参数时调用
- shouldComponentUpdate():返回一个布尔值。在组件接收到新的props或者state时被调用,判断是否重新渲染组件,可以在你确认不需要更新组件时可以将其设为false。
Demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>组件生命周期</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="../build/react.js"></script>
<script type="text/javascript" src="../build/react-dom.js"></script>
<script type="text/javascript" src="../build/browser.min.js"></script>
<script type="text/babel">
var Button = React.createClass({
getInitialState:function(){
return {
data:0
};
},
setNumber:function(){
this.setState({
data:this.state.data+1
});
},
render:function(){
return (
<div>
<button onClick = {this.setNumber}>增加</button>
<Content myNumber = {this.state.data}/>
</div>
);
}
})
var Content = React.createClass({
componentWillMount:function() {
console.log('Component WILL MOUNT!')
},
componentDidMount:function() {
console.log('Component DID MOUNT!')
},
componentWillReceiveProps:function(newProps) {
console.log('Component WILL RECIEVE PROPS!')
},
shouldComponentUpdate:function(newProps, newState) {
return true;
},
componentWillUpdate:function(nextProps, nextState) {
console.log('Component WILL UPDATE!');
},
componentDidUpdate:function(prevProps, prevState) {
console.log('Component DID UPDATE!')
},
componentWillUnmount:function() {
console.log('Component WILL UNMOUNT!')
},
render:function(){
return (
<div>
<h1>{this.props.myNumber}</h1>
</div>
);
}
});
ReactDOM.render(
<Button />,
document.getElementById("example")
);
</script>
</body>
</html>
React AJAX
React 组件的数据可以通过 componentDidMount 方法中的 Ajax 来获取,当从服务端获取数据库可以将数据存储在 state 中,再用 this.setState 方法重新渲染 UI。
当使用异步加载数据时,在组件卸载前使用 componentWillUnmount 来取消未完成的请求。
Demo:
获取 Github 用户最新 gist 共享描述
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>reactAjax</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="build/react.js"></script>
<script type="text/javascript" src="build/react-dom.js"></script>
<script type="text/javascript" src="build/browser.min.js"></script>
<script type="text/javascript" src="build/jquery.min.js"></script>
<script type="text/babel">
var UserGist = React.createClass({
getInitialState:function(){
return {
username:'',
lastGistUrl:''
};
},
componentDidMount:function(){
this.serverRequest = $.get(this.props.source,function(data){
var lastGist = data[0];
this.setState({
username:lastGist.owner.login,
lastGistUrl:lastGist.html_url
});
}.bind(this));
},
componentWillUnmount:function(){
this.serverRequest.abort();
},
render:function(){
return (
<div>
<h1>{this.state.username}用户最新的 Gist 共享地址:</h1>
<a href={this.state.lastGistUrl}>{this.state.lastGistUrl}</a>
</div>
);
}
});
ReactDOM.render(
<UserGist source = "https://api.github.com/users/octocat/gists"/>,
document.getElementById("example")
);
</script>
</body>
</html>
React 表单与事件
Demo1:
这里创建了两个组件,子组件和父组件
onChange 方法将触发 state 的更新并将更新的值传递到子组件的输入框的 value 上来重新渲染界面。
这里需要在父组件通过创建事件句柄 (handleChange) ,并作为属性 (这里是upDataState) 传递到子组件上。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>react事件和表单</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="../../build/react.js"></script>
<script type="text/javascript" src="../../build/react-dom.js"></script>
<script type="text/javascript" src="../../build/browser.min.js"></script>
<script type="text/babel">
var Content = React.createClass({
render:function(){
return (
<div>
<input type="text" value = {this.props.myData} onChange = {this.props.upDataState}/>
<h1>{this.props.myData}</h1>
</div>
);
}
});
var HelloMessage=React.createClass({
getInitialState:function(){
return {value:'hello react'};
},
handleChange:function(event){
this.setState({value:event.target.value});
},
render:function(){
var value=this.state.value;
return (
<div>
<Content myData = {value} upDataState = {this.handleChange}/>
</div>
);
}
});
ReactDOM.render(
<HelloMessage />,
document.getElementById('example')
);
</script>
</body>
</html>
当从子组件中更新父组件的 state 时,你需要在父组件通过创建事件句柄 (handleChange) ,并作为 属性 (updataState) 传递到你的子组件上。
Demo2:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>react事件与表单</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="../../build/react.js"></script>
<script type="text/javascript" src="../../build/react-dom.js"></script>
<script type="text/javascript" src="../../build/browser.min.js"></script>
<script type="text/babel">
var Content = React.createClass({
render:function(){
return (
<div>
<button onClick = {this.props.updataState}>点击改变</button>
<h1>{this.props.myData}</h1>
</div>
);
}
});
var HelloMessage = React.createClass({
getInitialState:function(){
return {
value:"Hello React"
}
},
handleClick:function(){
this.setState({
value:"Hello World"
});
},
render:function(){
var value = this.state.value;
return (
<div>
<Content myData = {value} updataState ={this.handleClick}/>
</div>
);
}
});
ReactDOM.render(
<HelloMessage />,
document.getElementById("example")
);
</script>
</body>
</html>
React Refs
React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。
这个特殊的属性允许你引用 render() 返回的相应的支撑实例。这样就可以确保在任何时间总是拿到正确的实例。
通过使用 this 来获取当前 React 组件,或使用 ref 来获取组件的引用
当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
Demo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>React Ref</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="../build/react.js"></script>
<script type="text/javascript" src="../build/react-dom.js"></script>
<script type="text/javascript" src="../build/browser.min.js"></script>
<script type="text/babel">
var MyComponent = React.createClass({
handleClick:function(){
this.refs.myInput.focus();
},
render:function(){
return (
<div>
<input type="text" ref = "myInput" />
<input type="button" value="点我输入框获取焦点" onClick = {this.handleClick}/>
</div>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById("example")
);
</script>
</body>
</html>