什么是react?
- React 是一个用于构建用户界面的 Javascript 库。
- React 主要用于构建UI,可以认为 React 是 MVC 中的 V(视图)。
- React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,于 2013 年 5 月开源。
- React 拥有较高的性能,代码逻辑非常简单,现在有越来越多的人开始关注和使用它。
react特性
声明式设计
React采用声明范式,可以轻松描述应用。无须使用变量,避免了创建和修改状态,使最终的代码简单易于维护。(区别于命令式编程,它的特点就是告诉计算机做什么,但是没有怎么做)
//命令式编程
var array = [1,2,3,4,5]
var total = 0
for(var i = 0; i < array.length; i++) {
total *= numbers[i]
}
console.log(total) //=> 120
//声明式编程
var array = [1,2,3,4,5]
var total = array.reduce(function(sum, n) {
return sum * n
});
console.log(total) //=> 120
//相比于命令式编程,声明式编程最大的特点是没有"if"、"for"这类的控制语句,而是直接给一个函数,并且声明一个变量接收函数执行结果,让计算机把数据当作参数放进去执行一遍。
高效(虚拟DOM)
React通过对DOM的模拟(虚拟DOM,Virtual DOM),最大限度地减少与DOM的交互。(核心是通过DIFF算法比较改变前后的DOM区别,然后用最少的DOM语句对DOM进行操作)
- React非常快速是因为它从不直接操作DOM。
- 虚拟DOM是在DOM的基础上建立了一个抽象层,对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟DOM,最后再批量同步到DOM中。
- 在React中,render执行的结果得到的并不是真正的DOM节点,而仅仅是JavaScript对象,称之为虚拟DOM。
- 虚拟DOM具有批处理和高效的Diff算法,可以无需担心性能问题而随时“刷新”整个页面,因为虚拟DOM可以确保只对界面上真正变化的部分进行实际的DOM操作。
传统App(innerHTML:render html字符串 + 重新创建所有 DOM 元素):
React App(虚拟DOM:render 虚拟DOM + diff + 更新必要的 DOM 元素):
虚拟DOM的原理
React会在内存中维护一个虚拟DOM树,对这个树进行读或写,实际上是对虚拟DOM进行。当数据变化时,React会自动更新虚拟DOM,然后将新的虚拟DOM和旧的虚拟DOM进行对比,找到变更的部分,得出一个diff,然后将diff放到一个队列里,最终批量更新这些diff到DOM中。
创建虚拟DOM的两种方式:
//纯js(一般不用)
React.createElement('h1', {id:'myTitle'}, title)
//JSX
<h1 id='myTitle'>{title}</h1>
<div id='example1'></div>
<div id='example2'></div>
灵活
React可以与已知的库或框架很好地配合。
Ant Design
一个可以用来快速设计后台 / 内部应用的 UI 库。它拥有一个社区、大量支持文档,还有一个带有预制模板的单独项目AntDesignPro。
React-Bootstrap
一个具有 Twitter 的 Bootstrap 的观感的 React 组件库。它的极简风格在社区中有很高的热度。
React-Transition-Group
React-Transition-Group提供了用于定义动画的简单组件,该库并未定义样式本身,而是以有用的方式操作DOM,从而使过渡和动画的实现更加舒适。
React-Redux
React-Redux是Redux的官方React绑定库。它能够使你的React组件从Redux store中读取数据,并且向store分发actions以更新数据。
JSX
JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但建议使用它。
JSX 即Javascript XML,它是对JavaScript 语法扩展。React 使用 JSX 来替代常规的 JavaScript。你也可以认为JSX其实就是JavaScript。当遇到<,JSX就当HTML解析,遇到{就当JavaScript解析。
JSX优点:
- JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
- 它是类型安全的,在编译过程中就能发现错误。
- 使用 JSX 编写模板更加简单快速。
- JSX 可以防止注入攻击。(React DOM 在渲染所有输入内容之前,默认会进行转义。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击)
使用JSX注意事项:
- 支持表达式书写,只需要将表达式写到{}内即可。
ReactDOM.render(
<div>
<h1>{1+1}</h1>
</div>
,
document.getElementById('example')
);
- 不能使用if else,可以使用三元运算表达式替代。
ReactDOM.render(
<div>
<h1>{i == 1 ? 'True!' : 'False'}</h1>
</div>
,
document.getElementById('example')
);
- JSX 允许在模板中插入数组,数组会自动展开所有成员。也可以利用数组的map属性来对其进行数据列表渲染。
var arr = [
<h1>test1</h1>,
<h2>test2</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
var arr=["你是风儿","我是沙","缠缠绵绵到天涯"];
var list=arr.map(function(v){
return <h3>{v}</h3>
})
ReactDOM.render(
<div>
{list}
</div>,
document.querySelector("#wrap")
)
- JSX 语法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用
camelCase(小驼峰命名)来定义属性的名称,而不使用 HTML 属性名称的命名约定。
例如,JSX 里的 class 变成了 className,而 tabindex 则变为 tabIndex。
- JSX中,可以通过使用引号,来将属性值指定为字符串字面量。也可以使用大括号,来在属性值中插入一个 JavaScript 表达式。
const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;
组件化
通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
React 的组件可以定义为 函数(function,无状态组件)或class(继承React.Component,有状态组件) 的形式。
//函数组件
function HelloMessage(props) {
return <h1>Hello World!</h1>;
}
//ES6 class 定义的组件
class Welcome extends React.Component {
render() {
return <h1>Hello World!</h1>;
}
}
函数组件和类组件的区别
- 两者最明显的不同就是在语法上,函数组件是一个纯函数,它接收一个
props对象返回一个react元素。而类组件需要去继承React.Component并且创建render函数返回react元素,这将会要更多的代码,虽然它们实现的效果相同。 - 类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。
- 函数组件不能访问
this对象,无法访问生命周期的方法,没有状态state。 - 类组件有
this,有生命周期,有状态state。 - 无状态(函数)组件只能访问输入的
props,同样的props会得到同样的渲染结果,不会有副作用。 - 函数组件的性能比类组件的性能要高,不知道用什么组件类型时,推荐用
React.FC。
单向响应的数据流
React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
React 单向数据流(自顶向下),数据主要从父节点传递到子节点(通过props)。如果顶层(父级)的某个props改变了,React会重渲染所有的子节点。
父子组件沟通
- 父组件更新组件状态,传递
props,子组件更新。 - 子组件更新父组件状态 ,父组件传递回调函数给子组件, 子组件调用触发。
class Child extends React.Component{
constructor(props){
super(props);
this.state = {}
}
render(){
return (
<div>
{this.props.text}
<br />
<button onClick={this.props.refreshParent}>
更新父组件
</button>
</div>
)
}
}
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {}
}
refreshChild(){
return (e)=>{
this.setState({
childText: "父组件沟通子组件成功",
})
}
}
refreshParent(){
this.setState({
parentText: "子组件沟通父组件成功",
})
}
render(){
return (
<div>
<h1>父子组件沟通</h1>
<button onClick={this.refreshChild()} >
更新子组件
</button>
<Child
text={this.state.childText || "子组件未更新"}
refreshParent={this.refreshParent.bind(this)}
/>
{this.state.parentText || "父组件未更新"}
</div>
)
}
}
兄弟组件沟通
当两个组件有相同的父组件时,就称为兄弟组件。按照React单向数据流方式,需要借助父组件进行传递,通过父组件回调函数改变兄弟组件的props。
- 通过
props传递父组件回调函数。 - 如果组件层次太深,React提供了一种上下文方式(挺方便的),可以让子组件直接访问祖先的数据或函数,无需从祖先组件一层层地传递数据到子组件中。
class Brother1 extends React.Component{
constructor(props){
super(props);
this.state = {}
}
render(){
return (
<div>
<button onClick={this.props.refresh}>
更新兄弟组件
</button>
</div>
)
}
}
class Brother2 extends React.Component{
constructor(props){
super(props);
this.state = {}
}
render(){
return (
<div>
{this.props.text || "兄弟组件未更新"}
</div>
)
}
}
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {}
}
refresh(){
return (e)=>{
this.setState({
text: "兄弟组件沟通成功",
})
}
}
render(){
return (
<div>
<h2>兄弟组件沟通</h2>
<Brother1 refresh={this.refresh()}/>
<Brother2 text={this.state.text}/>
</div>
)
}
}
全局事件
当项目复杂,组件间层次越来越深,可以使用全局事件来进行组件间的通信,官网推荐Flux(Facebook官方出的),还有Relay、Redux、trandux等第三方类库。这些框架思想都一致,都是统一管理组件state变化情况,达到数据可控目的。