1、ReactJS简介
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
2、ReactJS的背景和原理
在Web开发中,我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因(如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技能的重要指标)。
React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A变成B,然后又从B变成A,React会认为UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操作的仅仅是Diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。
如果你像在90年代那样写过服务器端Render的纯Web页面那么应该知道,服务器端所要做的就是根据数据Render出HTML送到浏览器端。如果这时因为用户的一个点击需要改变某个状态文字,那么也是通过刷新整个页面来完成的。服务器端并不需要知道是哪一小段HTML发生了变化,而只需要根据数据刷新整个页面。换句话说,任何UI的变化都是通过整体刷新来完成的。而React将这种开发模式以高性能的方式带到了前端,每做一点界面的更新,你都可以认为刷新了整个页面。至于如何进行局部更新以保证性能,则是React框架要完成的事情。
借用Facebook介绍React的视频中聊天应用的例子,当一条新的消息过来时,你的开发过程需要知道哪条数据过来了,如何将新的DOM结点添加到当前DOM树上;而基于React的开发思路,你永远只需要关心数据整体,两次数据之间的UI如何变化,则完全交给框架去做。可以看到,使用React大大降低了逻辑复杂性,意味着开发难度降低,可能产生Bug的机会也更少。
3、组件化
虚拟DOM(virtual-dom)不仅带来了简单的UI开发逻辑,同时也带来了组件化开发的思想,所谓组件,即封装起来的具有独立功能的UI部件。React推荐以组件的方式去重新思考UI构成,将UI上每一个功能相对独立的模块定义成组件,然后将小的组件通过组合或者嵌套的方式构成大的组件,最终完成整体UI的构建。例如,Facebook的instagram.com整站都采用了React来开发,整个页面就是一个大的组件,其中包含了嵌套的大量其它组件。
如果说MVC的思想让你做到视图-数据-控制器的分离,那么组件化的思考方式则是带来了UI功能模块之间的分离。我们通过一个典型的Blog评论界面来看MVC和组件化开发思路的区别。
对于MVC开发模式来说,开发者将三者定义成不同的类,实现了表现,数据,控制的分离。开发者更多的是从技术的角度来对UI进行拆分,实现松耦合。
对于React而言,则完全是一个新的思路,开发者从功能的角度出发,将UI分成不同的组件,每个组件都独立封装。
在React中,你按照界面模块自然划分的方式来组织和编写你的代码,对于评论界面而言,整个UI是一个通过小组件构成的大组件,每个组件只关心自己部分的逻辑,彼此独立。
React认为一个组件应该具有如下特征:
(1)可组合(Composeable):一个组件易于和其它组件一起使用,或者嵌套在另一个组件内部。如果一个组件内部创建了另一个组件,那么说父组件拥有(own)它创建的子组件,通过这个特性,一个复杂的UI可以拆分成多个简单的UI组件。
(2)可重用(Reusable):每个组件都是具有独立功能的,它可以被使用在多个UI场景。
(3)可维护(Maintainable):每个小的组件仅仅包含自身的逻辑,更容易被理解和维护。
4、JSX简介
HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX(JavaScript and XML) 的语法,JSX,是一种 JavaScript 的语法扩展,它允许 HTML 与 JavaScript 的混写。JSX是facebook为React框架开发的一套语法糖,语法糖又叫做糖衣语法,是指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用,它主要的目的是增加程序的可读性,从而减少程序代码错处的机会。JSX就是JS的一种语法糖,类似的还有CoffeeScript、TypeScript,最终它们都会被解析成JS才能被浏览器理解和执行,如果不解析浏览器是没有办法识别它们的,这也是所有语法糖略有不足的地方。
const element = <h1>Hello, world!</h1>;
上面这种看起来可能有些奇怪的标签语法既不是字符串也不是HTML,被称为 JSX,JSX带来的一大便利就是我们可以直接在JS里面写类DOM的结构,比我们用原生的JS去拼接字符串,然后再用正则替换等方式来渲染模板方便和简单太多了。推荐在 React 中使用 JSX 来描述用户界面。JSX 用来声明 React 当中的元素, 乍看起来可能比较像是模版语言,但事实上它完全是在 JavaScript 内部实现的。
你可以任意地在 JSX 当中使用 JavaScript 表达式,在 JSX 当中的表达式要包含在大括号里。例子如下:
const names = ['Jack', 'Tom', 'Alice'];
const element = (
<div>
{ names.map(function (name) { return <div>Hello, {name}!</div>}) }
</div>
);
在书写 JSX 的时候一般都会带上换行和缩进,这样可以增强代码的可读性。与此同时,推荐在 JSX 代码的外面扩上一个小括号,这样可以防止 分号自动插入 的bug。
上面我们声明了一个names数组,然后遍历names数组在前面加上Hello,生成了element数组。JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员。JSX 本身其实也是一种表达式,在编译之后,JSX 其实会被转化为普通的 JavaScript 对象。代码如下:
import React from 'react';
import ReactDOM from 'react-dom';
const names = ['Jack', 'Tom', 'Alice'];
const element = names.map(function (name) { return <div>Hello, {name}!</div>});
ReactDOM.render(
element,
document.getElementById('root')
);
显示结果如下:
JSX属性:
你可以使用引号来定义以字符串为值的属性:
const element = <div tabIndex="0"></div>;
也可以使用大括号来定义以 JavaScript 表达式为值的属性:
const element = <img src={user.avatarUrl}></img>;
切记当使用了大括号包裹的 JavaScript 表达式时就不要再到外面套引号了。JSX 会将引号当中的内容识别为字符串而不是表达式
5、ReactJS组件
组件可以将UI切分成一些的独立的、可复用的部件,这样你就只需专注于构建每一个单独的部件。
组件从概念上看就像是函数,它可以接收任意的输入值(称之为“props”),并返回一个需要在页面上展示的React元素
定义一个组件最简单的方式是使用JavaScript函数,函数定义组件:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
该函数是一个有效的React组件,它接收一个单一的“props”对象并返回了一个React元素。我们之所以称这种类型的组件为函数定义组件,是因为从字面上来看,它就是一个JavaScript函数。
你也可以使用 ES6 class来定义一个组件,类定义组件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
上面两个组件在React中是相同的。
ReactJS是基于组件化的开发,React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
定义组件时有几点需要注意:
创建的组件名称首字母必须大写。
为元素添加css的class时,要用className。
组件的style属性的设置方式也值得注意,要写成
style={{width: this.state.witdh}}
而不是style="opacity:{this.state.opacity};"
。组件的返回值只能有一个根元素。
组件的生命周期:如同人有生老病死,自然界有日月更替。每个组件在网页中也会被创建、更新和删除,如同有生命的机体一样。
React严格定义了组件的生命周期,生命周期可能会经历如下三个过程:
装载过程(Mount),也就是把组件第一次在DOM树中渲染的过程;
更新过程(Update),当组件被重新渲染的过程。
卸载过程(Unmount),组件从DOM中删除的过程。
三种不同的过程,React库会依次调用组件的一些成员函数,这些函数称为生命周期函数。所以,要定制一个React组件,实际上就是定制这些生命周期函数。
1、装载过程(Mount):
装载过程,当组件第一次被渲染的时候,依次调用如下函数:
constructor
getInitialState
getDefaultProps
componentWillMount
render
componentDidMount
2、更新过程(Update):
当组件被装载到DOM树上之后,用户在网页上可以看到组件的第一印象,但是要提供更好的交互体验,就要让该组件可以随着用户操作改变展现的内容,当props或者state被修改的时候,就会引发组件的更新过程。
更新过程会依次调用下面的生命周期函数,其中render函数和装载过程一样,没有差别:
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
注意:并不是所有的更新过程都会执行全部函数。
3、卸载过程(Unmount)
React组件的卸载过程只涉及一个函数componentWillUnmount,当React组件要从DOM树上删除掉之前,对应的componentWillUnmount函数会被调用,所以这个函数适合做一些清理的工作。
和装载过程与更新过程不一样,这个函数没有配对的Did函数,就一个函数,因为卸载完就完了,没有“卸载完再做的事情”。
componentWillUnmount中的工作往往和componentDidMount有关,比如,在componentDidMount中用非React的方法创造了一些DOM元素,如果撒手不管可能会造成内存泄漏,那就需要在componentWillUnmount中把这些创造的DOM元素清理掉。
6、ReactJS小结
ReactJs是基于组件化的开发,所以最终你的页面应该是由若干个小组件组成的大组件。
可以通过属性,将值传递到组件内部,同理也可以通过属性将内部的结果传递到父级组件(留给大家研究);要对某些值的变化做DOM操作的,要把这些值放到state中。
为组件添加外部css样式时,类名应该写成className而不是class;添加内部样式时,应该是style={{opacity: this.state.opacity}}而不是style="opacity:{this.state.opacity};"。
组件名称首字母必须大写。
组件的返回值只能有一个根元素。
变量名用{}包裹,且不能加双引号。