1. React概览前端
最初听到React而还未深刻了解它时,大多数人可能和个人想法同样:难道又是一个新的MVC/MVVM前端framework?深刻了解后发现不是这么一回事,React关注的东西很单纯,就是view,而且它也确实解决了前端目前的一些问题,好比view代码的复用,封装组件。应该说React提出了一些新的东西,让前端开发人员有机会从新审视view层的开发策略。react
先来快速看一个React的简单例子:git
HelloMessage组件:github
)web
使用HelloMessage组件:算法
固然别忘了还有组件要挂载到的节点:数组
效果:缓存
这个例子很好理解,在HelloMessage.react.js中,咱们用React建立了一个名叫HelloMessage的UI组件,它的输出是数据结构
1 <div>Hello, {this.props.name}!</div>
this.props.name就是在使用HelloMessage组件时传入的name属性:架构
1 <HelloMessage name="Tom"/>
因而会显示 "Hello, Tom!" 就很好理解了,很简单吧?
要跑起这个例子咱们还须要一些引入一些库和配置一些自动化构建的工做流,这部分比较枯燥,放在最后给出,想立刻动手试一试的童鞋能够先跳转到[环境搭建]一节中。
React 的核心思想是:封装组件
简单来讲就是,各个组件维护本身的状态(state)和UI,当状态变动时,自动从新渲染整个组件。这种作法的一个好处就在于咱们能够获得一个个UI组件,而且它们的状态是自管理的。好比当用户在某个UI组件上触发一个事件时,咱们没必要编写查找DOM的代码去找到要操做的DOM元素,再施加操做;取而代之的是咱们在编写组件时,就已经写好组件能响应什么事件,要如何响应事件。没有没繁琐的DOM查找,代码也变得清晰不少。
React基本上由如下部分组成:
1. 组件
2. JSX
3. Virtual DOM
4. Data Flow
组件
组件是React的核心,刚才的HelloMessage就是一个组件,组件包含两个核心概念,props和state。props是组件的配置属性,是不会变化的,就像刚才咱们看到的示例,在声明组件时咱们传入了name="Tom",就是指定了HelloMessage这个组件的name属性值为"Tom",能够定义多个不一样的组件属性。state是组件的状态,是会变化的,当state变化时组件会自动从新渲染本身。有一篇讨论React的[文章](http://www.infoq.com/cn/articles/react-art-of-simplity/)中谈到组件,如下引用这篇文章的这段话:
所谓组件,就是状态机器
React将用户界面看作简单的状态机器。当组件处于某个状态时,那么就输出这个状态对应的界面。经过这种方式,就很容易去保证界面的一致性。
在React中,你简单的去更新某个组件的状态,而后输出基于新状态的整个界面。React负责以最高效的方式去比较两个界面并更新DOM树。
JSX
可能有人会发现使用React时JS代码的写法有点奇怪,直接把HTML嵌入在JS中,固然JS引擎是没法解释这种语法的,必须由咱们把JSX代码编译输出后,才是JS引擎可读的代码。JSX是一种把HTML封装成组件的有效手段,它把组件的数据和UI融合在一块儿,使组件化成为可能。这里可能有童鞋会表示疑惑,咱们在作界面开发的时候不是常常讲要"表现和逻辑分离"吗,为何React把表现和逻辑整合在一块儿就没问题呢?好吧,这是个问题,咱们放在后面再来讨论。
Virtual DOM
当组件state发生变化的时候,React会调用组件的render方法自动从新渲染组件UI。能够预想到的一种状况是,若是某个组件很大,其中可能还包含了不少其余组件,那一次从新渲染代价将会很大(由于要遍历这个组件的整个DOM结构进行渲染),不过React对此作了优化,React在内部实现了一个Virtual DOM,组件的DOM结构映射到这个Virtual DOM上,React在这个Virtual DOM上实现了一个diff算法,简单来讲就是React会使用这个diff算法来判断某个组件是否须要更新,只更新有变化的组件。这个更新会先发生在Virtual DOM上,Virtual DOM是内存中的一个数据结构,所以更新起来很快,最后再真正地去更新真实的DOM。
Data Flow
React推崇一种叫作"单向数据绑定"的模式, 结合Flux这种应用架构构建客户端web应用。
2. JSX
传统的MVC是将模板写成模板文件独立放在某个地方,在须要时去引用这个模板,这样作确实是把表现成单独分离了,可是这又带来一些新的问题:咱们要用什么姿式引用这些模板,或者说我要怎么把数据注入模板,模板存放在哪里等问题。也就是说这个模板和代码逻辑看似是分离的(物理上的分离),其实仍是耦合在一块儿的(逻辑上是耦合的)。为了实现组件化,React引入了JSX的概念。
React实现了组件模板和组件逻辑关联,因此才有了JSX这种语法,把HTML模板直接嵌入到JS中,编译输出后可用。能够认为JSX是一种"中间层",或者说"胶水层",它把HTML模板和JS逻辑代码粘合在一块儿。
JSX是可选的
React会解析JSX代码来生成JS引擎可读的代码,所以最后的输出代码都是JS引擎可读的,也就是咱们日常用的JS代码,所以若是有必要能够无视JSX,直接用React提供的DOM方法来构建HTML模板,好比下面这行代码是用JSX写:
1 <a href="http://facebook.github.io/react/">Hello!</a>
也可使用React.createElement来构建DOM树,第一个参数是标签名,第二个参数是属性对象,第三个参数是子元素:
1 React.createElement('a', {href: 'http://facebook.github.io/react/'}, 'Hello!')
利用JSX来写HTML模板,能够用原生的HTML标签,也能够像使用原生标签那样引用React组件。React约定经过首字母大小写来区分这二者。原生的HTML标签和平时同样,都是小写的,React组件首字母须要大写。使用HTML标签:
1 var render = require('react-dom').render; 2 var myDivElement = <div className="foo" />; 3 render(myDivElement, document.body);
使用React组件:
1 var render = require('react-dom').render; 2 var MyComponent = require('./MyComponet'); 3 var myElement = <MyComponent someProperty={true} />; 4 render(myElement, document.body);
使用JavaScript表达式
能够在属性值和子组件中使用JavaScript表达式,使用{}包裹表达式。稍微改造一下HelloMessage的例子,
在属性中使用JavaScript表达式:
1 //HelloMessage.react.js 2 var React = require('react'); 3 4 var HelloMessage = React.createClass({ 5 render: function() { 6 return < div > Hello, 7 { 8 this.props.sex === 'm' ? 'Mr.': 'Mrs.' 9 } { 10 this.props.fname 11 } ! </div>; 12 } 13 }); 14 15 module.exports = HelloMessage;/
1 //使用HelloMessage 2 var React = require('react'); 3 var render = require('react-dom').render; 4 var HelloMessage = require('./components/HelloMessage.react.js'); 5 6 var num = 1; 7 8 render( < HelloMessage fname = 'Smith'sex = { 9 num === 0 ? 'm': 'f' 10 } 11 />, 12 document.getElementById('helloMessage') 13 );/
输出:
Hello, Mr. Smith!
在子组件中使用JavaScript表达式:
1 //HelloMessage.react.js 2 var React = require('react'); 3 4 var HelloMessage = React.createClass({ 5 render: function() { 6 return < div > Hello, 7 { 8 this.props.sex === 'm' ? 'Mr.': 'Mrs.' 9 } { 10 this.props.fname 11 } ! </div>; 12 } 13 }); 14 15 module.exports = HelloMessage;/
1 //使用HelloMessage 2 var React = require('react'); 3 var render = require('react-dom').render; 4 var HelloMessage = require('./components/HelloMessage.react.js'); 5 6 render( < HelloMessage fname = 'Smith'sex = 'm' / >, document.getElementById('helloMessage'));
输出:
Hello, Mrs. Smith!
注释
在JSX中使用注释和在JS差很少,惟一要注意的是在一个组件的子元素位置使用注释要用{}包裹起来,看下面这个能够工做的例子:
1 var React = require('react'); 2 3 var HelloMessage = React.createClass({ 4 render: function() { 5 return ( 6 /*这里是注释1*/ 7 < div 8 /*这里 9 是 10 注释2*/ 11 > { 12 /*这里是注释3*/ 13 } 14 Hello, { 15 this.props.sex === 'm' ? 'Mr.': 'Mrs.' 16 } { 17 this.props.fname 18 } ! </div> 19 ); 20 } 21 }); 22 23 module.exports = HelloMessage;/
注意到只有注释3须要被包裹在{}内,由于存在于一个子元素的位置。
属性扩散(ES6支持)
属性扩散使用了ES6起支持的扩展运算符,扩展运算符用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值,使用属性扩散能够知足咱们懒惰的心理:
1 var React = require('react'); 2 var render = require('react-dom').render; 3 var HelloMessage = require('./components/HelloMessage.react.js'); 4 5 var props = { 6 fname: 'Smith', 7 sex: 'm' 8 }; 9 10 render( < HelloMessage {...props 11 } 12 />, 13 document.getElementById('helloMessage') 14 ); 15 16 /
输出:
Hello, Mr. Smith!
还能够显式覆盖属性扩散的值:
1 var React = require('react'); 2 var render = require('react-dom').render; 3 var HelloMessage = require('./components/HelloMessage.react.js'); 4 5 var props = { 6 fname: 'Smith', 7 sex: 'm' 8 }; 9 10 render( < HelloMessage {...props 11 } 12 fname = 'Johnson'sex = 'f' / >, document.getElementById('helloMessage'));
这里后面的fname和sex会覆盖前面的值,输出:
Hello, Mrs. Johnson!
JSX和HTML的差别
1. 在JSX中,class要写成className。
2. JSX中能够自定义标签和属性。
3. JSX支持JavaScript表达式。
3. React组件
React应用是构建在组件之上的。组件的两个核心概念:
1. props
2. state
组件经过props和state生成最终的HTML,须要注意的是,*经过组件生成的HTML结构只能有一个根节点*。
props
props就是一个组件的属性,咱们能够认为属性是不变的,一旦经过属性设置传入组件,就不该该去改变props,虽然对于一个JS对象咱们能够改变几乎任何东西。
state
state是组件的状态,以前说到组件是一个状态机,根据state改变本身的UI。组件state一旦发生变化,组件会自动调用render从新渲染UI,这个动做会经过this.setState方法触发。
如何划分props和state
应该尽量保持组件状态的数量越少越好,状态越少组件越容易管理。那些不会变化的数据、变化没必要更新UI的数据,均可以归为props;须要变化的、变化须要更新UI的则归为state。
无状态组件
一些很简单的组件可能并不须要state,只须要props便可渲染组件。在ES6中能够用纯函数(没有反作用,无状态)来定义:
1 const HelloMessage = (props) => <div> Hello, {props.name}!</div>; 2 render(<HelloMessage name="Smith" />, mountNode);
组件的生命周期
一些重要函数
1. getInitialState:
初始化this.state的值,只在组件装载以前调用一次。
2. getDefaultProps
只在组件建立时调用一次并缓存返回的对象(即在 React.createClass 以后就会调用)。在组件加载以后,这个方法的返回结果会保证当访问this.props的属性时,就算在JSX中没有设置相应属性,也老是能取到一个默认的值。
3. render
每一个组件都必须实现的方法,用来构造组件的HTML结构。能够返回null或者false,此时ReactDOM.findDOMNode(this)会返回null。
生命周期函数:
装载组件触发
1. componentWillMount
只在组件装载以前调用一次,在render以前调用,能够在这里面调用setState改变状态,而且不会致使额外的一次render。
2. componentDidMount
只会在组件装载完成以后调用一次,在render以后调用,从这里开始能够经过ReactDOM.findDOMNode(this)获取到组件的DOM节点。
更新组件触发
这些方法不会在首次render组件的周期调用
1. componentWillReceiveProps
2. shouldComponentUpdate
3. componentWillUpdate
4. componentDidUpdate
DOM操做
1. findDOMNode
当组件加载到页面上之后,就能够用react-dom的findDOMNode方法来得到组件对用的DOM元素了,注意,findDOMNode不能用在无状态组件上。
2. refs
还有一种方法是在要引用的DOM元素上设置一个ref属性,经过this.refs.name能够引用到相应的对象。若是ref是设置在原生HTML元素上,它拿到的就是DOM元素,若是设置在自定义组件上,它拿到的就是组件实例,这时候就须要经过findDOMNode来拿到组件的DOM元素。须要注意的是,刚才提到的无状态组件没有实例,它就是个函数,因此ref属性不能设置在无状态组件上。由于无状态组件没有实例方法,不须要用ref去拿实例而后调用实例方法,若是真的想得到无状态组件的DOM元素的时候,须要用一个有状态组件封装一层,而后用this.refs.name和findDOMNode去获取DOM。
总结
1. 可使用ref调用组件内子组件的实例方法,好比this.refs.myInput.focus();
2. refs 是访问到组件内部 DOM 节点惟一可靠的方法。
3. refs 会自动销毁对子组件的引用(当子组件删除时)
注意事项
1. 不要在render或者render以前访问refs
2. 不要滥用refs,好比只是用它来按照传统的方式操做界面UI:找到DOM->更新DOM
组件间通讯
1. 父子组件间通讯
父子组件间通讯能够经过props属性来传递,在父组件中给子组件设置props,子组件就能够经过props访问到父组件的属性和方法。
2. 非父子组件间通讯
使用全局事件Pub/Sub模式,在componentDidMount里面订阅事件,在componentWillUnmount里面取消订阅,当收到事件触发的时候调用setState更新UI。