React是一个JavaScript语言的工具库,起源于Facebook的一个内部项目,最初用来构建Instagram的网站,并于2013年5月开源。
安装
对于React初学者而言,React官方提供了一个工具——create-react-app,目的是将开发者人员从配置中解放出来,从而能快速的进入到React应用的开发中。
具体安装过程如下:
npm install -g create-react-app 全局安装 creat-react-app 命令
create-react-app my-app 使用命令在当前目录下创建一个名为my-app的react应用
cd my-app 进入my-app项目文件夹
npm-start 执行该命令
之后会启动一个开发者模式的服务器,同时浏览器自动打开一个网页,指向http://localhost:3000/
界面如下图所示:
http://localhost:3000/第一个React应用就这样诞生了~
生成的文件夹目录结构,如下图所示:
项目目录结构开发过程中,我们主要关注src目录中的内容,而我们所创建项目的入口便是src/index.js文件,这里面的代码做了这样一件事,它渲染了一个名为App的组件,将其挂在到了id为root的DOM节点上。
src/index.js同时我们再来看看在src/App.js中,App组件是怎样创建的:
src/App.js可以看到代码的第一行我们从react库中引入了React和Component,Component作为所有组件的基类,提供了很多组件共有的功能,之后创建了一个名为App的组件类,然后通过export default App将其导出,并在在src/index.js中通过import导入,最后加以使用。
UI=render(data)
React的理念归结为一个公式:UI=render(data)
用户看到的UI界面,是一个函数——render的执行结果,只接受数据——data作为参数。这是一个纯函数,即输出只依赖于输入的函数,两次函数的调用如果输入相同,那么输出也绝对相同。如此一来,最终的用户界面,在render函数确定的情况下完全取决于输入数据。
因此对于开发者而言,重要的是区分,哪些是render,哪些是data,想要改变UI,要做的就是更新data
——《深入浅出React和Redux》
Virtual DOM
在上一节中,我们似乎很容易得出这样一个结论,每次更新UI界面,react都要进行重新渲染,这样会不会使得效率低下呢?
事实上并不会这样,React利用Virtual DOM让每次渲染都只渲染最少的DOM元素。Virtual DOM是对DOM树的抽象,它并不触及浏览器部分,只是存在于JavaScript空间的树形结构,每次在渲染React组件,React会对前后两次产生的Virtual DOM进行比较,最后只有发生了改变的地方会被重新渲染。
总而言之,React利用函数式编程的思维来解决用户界面渲染的问题,强制所有组件都以数据驱动渲染的模式进行开发。
JSX
要学习react首先就要了解JSX,它是JavaScript的一种扩展语法,使我们能够在JavaScript中编写类似HTML的代码。
JSX 的基本语法规则:遇到 HTML 标签(以<开头),就用 HTML 规则解析;遇到代码块(以{开头),就用 JavaScript 规则解析。
我们可以用{}将任意的JavaScript表达式嵌入到JSX中。
JSX注释也很简单,一般用花括号包围:
{/* 这里是注释 */}
React组件
React允许将代码封装成组件,然后像插入普通HTML标签那样,插入使用我们自定义的组件。自定义的组件要以大写字母开头,这样才能被识别为React组件。
组件也可以在标签中插入任意属性,特别注意:
class属性需要写成className,for属性需要写成htmlFor,这是因为class和for是 JavaScript 的保留字。对于属性的访问,可以通过this.props.xxx来访问对应的xxx属性。
每个组件必须有一个render方法,用于输出组件自身,同时输出的组件只能包含一个顶层标签。
举个例子:
名为Demo的组件在上面的例子中,我们创建了一个名为Demo的React组件,用来在界面输出Hello,my name is xxx的信息。而this.props.name就是对组件调用时传入的name属性的访问。最终界面输出如下图所示:
输出结果props & state
React组件的数据分为两种——props和state。其中props是组件的对外接口,state则是组件的内部状态。它们任何一个的改变都会引发组件的重新渲染。
props
props是从外部传递给组件的数据,在上一节的例子中,name就是传递给Demo组件的一个外部props,同时在组件内部,通过this.props.nam访问,这样我们就能很轻易的将外部的数据传递给组件内部。那相反,如果我们想要把内部的数据传递给外部该怎么办呢?
同样也是使用props,因为props的类型不限于纯数据,它还可以是函数,所以我们可以定义一个函数类型的props,相当于父组件交给了自组件一个回调函数。自组件在适当的时候调用这个函数,并传入必要的参数,父组件接收到参数后,对参数进行相应的逻辑处理。
举个例子:
Parent父组件在Parent父组件中,我们定义了一个changeResult的函数,它接收一个参数,最后计算出参数的二次幂,将结果result显示在界面中。同时,接收的这个参数是来自于Child组件,父组件在调用子组件时,将changeValue这个方法作为子组件的props传给了子组件。
Child子组件在Child子组件中,点击button会使得子组件中value值加1,并在此时调用this.props.changeValue,将新的value值传给父组件Parent。
最终的效果如下图所示:
初始值 点击button后的值protoType可以用来规范组件的接口,定义自己可以接收哪些类型或者符合哪些规则的prop。以上面的Child组件来说,我们要求它接收的changeReault是一个函数:
Child.propTypes = {
changeResult: PropTypes.func
};
上面的代码要求changeResult这个prop必须是个函数的类型。
state
state代表组件的内部状态,因为组件不能修改传入的props,所以为了记录自身的状态变化就得用到state。
组件的state通常在组件的构造函数中初始化,且state必须是一个对象。
constructor(props){
super(props);
this.state = {
value: 0
}
}
React还提供了一个defaultProps功能,用来制定默认的prop值,在特定的props值不是必须且没有被传入的情况下,会使用这个指定的默认值。
Child.defaultProps = {
changeResult: f => f //默认是一个什么都不做的函数
}
对state的修改不能直接通过this.state.xxx来修改,需要调用this.setState({xxx: newValue});来修改。因为this.setState会驱动组件进行重新渲染,而如果直接用this.state.xxx来修改,并不能触发。
React组件的生命周期
React定义了组件的生命周期,可分为一下三个过程:
装载过程:Mount —— 组件第一次在DOM树中渲染的过程
更新过程:Update —— 组件被重新渲染的过程
卸载过程:Unmount —— 组件从DOM中删除的过程
在不同的生命周期中,React会一次调用组件的一些函数,这些函数被称为生命周期函数。
装载过程
当组件第一次被挂载的时候会依次调用以下函数:
constructor
componentWillMount
render
componentDidMount
这几个函数从函数名就大概知道是什么了。
constructor构造函数,非必需,无状态组件可以不使用。在存在的情况下,constructor是生命周期中被调用的第一个函数,在这里我们可以初始化state,同时绑定成员函数的this环境。
componentWillMount,非必需,render函数之前被调用,与componentDidMount对称。同时componentWillMount可以在服务端和浏览器端被调用。
render,必须,它是React组件中最重要的函数,它返回一个JSX描述的结构,之后React来操作渲染过程,render函数是一个纯函数,完全根据this.state和this.props 来决定返回结果,因此在render函数中调用this.setState是错误的。
componentDidMount,非必需,只能在浏览器端被调用,componentDidMount函数并不是紧跟render函数后面被调用,它被调用时,render函数返回的东西已经引发来渲染,组件已经被“装载”到了DOM树上,此时可以放心的获取渲染出来的任何DOM,也可以通过ajax获取数据来填充组件的内容。
更新过程
更新过程会调用如下生命周期函数:
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
但并不是所有更新过程都会调用以上所有函数。
我们先来看看哪些情况会触发更新过程(默认shouldComponentUpdate返回true)
1. 父组件的render函数被调用
2. this.setState调用,但不是每调用一次就更新一次,有时react会合并操作,并最后一次性调用render更新
3. this.forceUpdate强制更新
而在这些情况下会依次调用的生命周期函数,如下图所示:
更新路径componentWillReceiveProps当父组件调用render时会触发,而不是仅仅只有props发生改变时才触发。
shouldComponentUpdate默认返回true,如果返回false则立即停止更新,如果恰当使用shouldComponentUpdate来控制是否继续更新,可以有效提高效率。
componentWillUpdate,当shouldComponentUpdate返回true,紧接着调用componentWillUpdate,之后是render,再之后是componentDidUpdate。
注意:通过this.setState函数引发的更新,并不是立刻改变state的值,其实,在调用shouldComponentUpdate时state的值依然是this.setState执行之前的值。
卸载过程
componentWillUnmount,当react组件需要从DOM树上删除掉之前会调用它,所以这个函数适合做一些清理工作。比如,在componentDidMount中用非react的方法创造了一些DOM元素,如果撒手不管的话会造成内存泄漏,那就需要在componentWillUnmount中将这些DOM卸载掉。
参考文章和书籍
http://www.jianshu.com/p/4784216b8194
《深入浅出React和Redux》
网友评论