有一段解释很透彻: 用脚本进行DOM操做的代价很昂贵。有个贴切的比喻,把DOM和JavaScript各自想象为一个岛屿,它们之间用收费桥梁链接,js每次访问DOM,都要途径这座桥,并交纳“过桥费”,访问DOM的次数越多,费用也就越高。 所以,推荐的作法是尽可能减小过桥的次数,努力待在ECMAScript岛上。 由于这个缘由react的虚拟dom就显得难能难得了,它创造了虚拟dom而且将它们储存起来,每当状态发生变化的时候就会创造新的虚拟节点和之前的进行对比(Diff算法),让变化的部分进行渲染。整个过程没有对dom进行获取和操做,只有一个渲染的过程,因此react说是一个ui框架。javascript
react的一个组件很明显的由dom视图和state数据组成,两个部分泾渭分明。 state是数据中心,它的状态决定着视图的状态。这时候发现彷佛和咱们一直推崇的MVC开发模式有点区别,没了Controller控制器,那用户交互怎么处理,数据变化谁来管理?然而这并非react所要关心的事情,它只负责ui的渲染。与其余框架监听数据动态改变dom不一样,react采用setState来控制视图的更新。setState会自动调用render函数,触发视图的从新渲染,若是仅仅只是state数据的变化而没有调用setState,并不会触发更新。 组件就是拥有独立功能的视图模块,许多小的组件组成一个大的组件,整个页面就是由一个个组件组合而成。它的好处是利于重复利用和维护。java
eact的diff算法用在什么地方呢?当组件更新的时候,react会建立一个新的虚拟dom树而且会和以前储存的dom树进行比较,这个比较多过程就用到了diff算法,因此组件初始化的时候是用不到的。react提出了一种假设,相同的节点具备相似的结构,而不一样的节点具备不一样的结构。在这种假设之上进行逐层的比较,若是发现对应的节点是不一样的,那就直接删除旧的节点以及它所包含的全部子节点而后替换成新的节点。若是是相同的节点,则只进行属性的更改。react
对于列表的diff算法稍有不一样,由于列表一般具备相同的结构,在对列表节点进行删除,插入,排序的时候,单个节点的总体操做远比一个个对比一个个替换要好得多,因此在建立列表的时候须要设置key值,这样react才能分清谁是谁。固然不写key值也能够,但这样一般会报出警告,通知咱们加上key值以提升react的性能。 webpack
组件的创造方法为React.createClass() ——创造一个类,react系统内部设计了一套类系统,利用它来创造react组件。但这并非必须的,咱们还能够用es6的class类来创造组件,这也是Facebook官方推荐的写法。git
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state ={
params:'',
MyList:[]
}
}
复制代码
这两种写法实现的功能同样可是原理倒是不一样,es6的class类能够看做是构造函数的一个语法糖,能够把它当成构造函数来看,extends实现了类之间的继承 —— 定义一个类MyComponent继承React.Component全部的属性和方法,组件的生命周期函数就是从这来的。 constructor是构造器,在实例化对象时调用,super调用了父类的constructor创造了父类的实例对象this,而后用子类的构造函数进行修改。这和es5的原型继承是不一样的,原型继承是先创造一个实例化对象this,而后再继承父级的原型方法。 当咱们使用组件时,实际上是对MyComponent类的实例化,只不过react对这个过程进行了封装,使其看起来像一个标签。 props访问父组件的属性 this.props.children 访问组件的孩子节点 能够用React.Children.map()遍历this.props.childrenes6
1.componentWillReceiveProps(nextProps) :组件初始化时不调用,组件接受新的props时调用。 2. shouldComponentUpdate(nextProps,nextState) :react性能优化很是重要的一环。组件接受新的state或者props时调用,咱们能够设置在此对比先后两个props和state是否相同,若是相同则返回false阻止更新,由于相同的属性状态必定会生成相同的dom树,这样就不须要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤为是在dom结构复杂的时候。不过调用this.forceUpdate会跳过此步骤。 3. componentWillUpdate(nextProps,nextState) :组件初始化时不调用,只有在组件将要更新时才调用,此时能够修改state 4. render() 5. componentDidUpdate() :组件初始化时不调用,组件更新完成后调用,此时能够获取dom节点。github
Router就是react的一个组件,并不会被渲染,只是一个建立内部路由规则的配置对象,根据匹配的路由地址展示相应的组件。 Router则对路由地址和组件进行绑定,Router具备嵌套功能,表示路由地址的包含关系,这和组件的嵌套没有直接联系。 Router能够向绑定的组件传递7个属性:children,history,location,params,route,routeParams,routes,每一个属性都包涵路由的相关的信息。比较经常使用的有children(以路由的包涵关系为区分的组件),location(包括地址,参数,地址切换方式,key值,hash值)。react-router提供Link标签,这只是对a标签的封装,值得注意的是,点击连接进行的跳转并非默认的方式,react-router阻止了a标签的默认行为并用pushState进行hash值的转变。切换页面的过程是在点击Link标签或者后退前进按钮时,会先发生url地址的转变,Router监听到地址的改变根据Route的path属性匹配到对应的组件,将state值改为对应的组件并调用setState触发render函数从新渲染dom。 当页面比较多时,项目就会变得愈来愈大,尤为对于单页面应用来讲,初次渲染的速度就会很慢,这时候就须要按需加载,只有切换到页面的时候才去加载对应的js文件。react配合webpack进行按需加载的方法很简单,Route的component改成getComponent,组件用require.ensure的方式获取,并在webpack中配置chunkFilename。web
const chooseProducts = (location, cb) => {
require.ensure([], require => {
cb(null, require('../Component/chooseProducts').default)
},'chooseProducts')
}
const helpCenter = (location, cb) => {
require.ensure([], require => {
cb(null, require('../Component/helpCenter').default)
},'helpCenter')
}
const saleRecord = (location, cb) => {
require.ensure([], require => {
cb(null, require('../Component/saleRecord').default)
},'saleRecord')
}
const RouteConfig = (
<Router history={history}>
<Route path="/" component={Roots}>
<IndexRoute component={index} />//首页
<Route path="index" component={index} />
<Route path="helpCenter" getComponent={helpCenter} />//帮助中心
<Route path="saleRecord" getComponent={saleRecord} />//销售记录
<Redirect from='*' to='/' />
</Route>
</Router>
);
复制代码
react推崇的是单向数据流,自上而下进行数据的传递,可是由下而上或者不在一条数据流上的组件之间的通讯就会变的复杂。解决通讯问题的方法不少,若是只是父子级关系,父级能够将一个回调函数看成属性传递给子级,子级能够直接调用函数从而和父级通讯。算法
组件层级嵌套到比较深,可使用上下文getChildContext来传递信息,这样在不须要将函数一层层往下传,任何一层的子级均可以经过this.context直接访问。npm
兄弟关系的组件之间没法直接通讯,它们只能利用同一层的上级做为中转站。而若是兄弟组件都是最高层的组件,为了可以让它们进行通讯,必须在它们外层再套一层组件,这个外层的组件起着保存数据,传递信息的做用,这其实就是redux所作的事情。
组件之间的信息还能够经过全局事件来传递。不一样页面能够经过参数传递数据,下个页面能够用location.param来获取。其实react自己很简单,难的在于如何优雅高效的实现组件之间数据的交流。
首先,redux并非必须的,它的做用至关于在顶层组件之上又加了一个组件,做用是进行逻辑运算、储存数据和实现组件尤为是顶层组件的通讯。若是组件之间的交流很少,逻辑不复杂,只是单纯的进行视图的渲染,这时候用回调,context就行,不必用redux,用了反而影响开发速度。可是若是组件交流特别频繁,逻辑很复杂,那redux的优点就特别明显了。 Redux 的react绑定库是基于 容器组件和展现组件相分离 的开发思想。
react-redux提供了connect和Provider两个好基友,它们一个将组件与redux关联起来,一个将store传给组件。组件经过dispatch发出action,store根据action的type属性调用对应的reducer并传入state和这个action,reducer对state进行处理并返回一个新的state放入store,connect监听到store发生变化,调用setState更新组件,此时组件的props也就跟着变化。 流程以下:
store是一个对象,有四个主要方法:
用于action的分发——在createStore中能够用middleware中间件对dispatch进行改造,好比当action传入dispatch会当即触发reducer,有些时候咱们不但愿它当即触发,而是等待异步操做完成以后再触发,这时候用redux-thunk对dispatch进行改造,之前只能传入一个对象,改造完成后能够传入一个函数,在这个函数里咱们手动dispatch一个action对象,这个过程是可控的,就实现了异步。
监听state的变化——这个函数在store调用dispatch时会注册一个listener监听state变化,当咱们须要知道state是否变化时能够调用,它返回一个函数,调用这个返回的函数能够注销监听
获取store中的state——当咱们用action触发reducer改变了state时,须要再拿到新的state里的数据,毕竟数据才是咱们想要的。getState主要在两个地方须要用到,一是在dispatch拿到action后store须要用它来获取state里的数据,并把这个数据传给reducer,这个过程是自动执行的,二是在咱们利用subscribe监听到state发生变化后调用它来获取新的state数据,若是作到这一步,说明咱们已经成功了
替换reducer,改变state修改的逻辑
action是一个对象,其中type属性是必须的,同时能够传入一些数据。action能够用actionCreator进行创造。dispatch就是把action对象发送出去。
官网上是这么定义的
Actions describe the fact that something happened, but don't specify how the application's state changes in response. This is the job of reducers.
reducer是一个函数,它接受一个state和一个action,根据action的type返回一个新的state。根据业务逻辑能够分为不少个reducer,而后经过combineReducers将它们合并,state树中有不少对象,每一个state对象对应一个reducer,state对象的名字能够在合并时定义。
const reducer =combineReducers({
a:doSometingWithA,
b:processB,
c:c
})
复制代码
其实它也是一个reducer,它接受整个state和一个action,而后将整个state拆分发送给对应的reducer进行处理,全部的reducer会收到相同的action,不过它们会根据action的type进行判断,有这个type就进行处理而后返回新的state,没有就返回默认值,而后这些分散的state又会整合在一块儿返回一个新的state树。
首先调用store.dispatch将action做为参数传入,同时用调用getState获取当前的状态树state并注册subscribe的listener监听state变化,再调用combineReducers并将获取的state和action传入。combineReducers会将传入的state和action传给全部reducer,reducer会根据state的key值获取与本身对应的state,并根据action的type返回新的state,触发state树的更新,咱们调用subscribe监听到state发生变化后用getState获取新的state数据。 redux的state和react的state二者彻底没有关系,除了名字同样。
若只使用redux,流程是这样:
component --> dispatch(action) --> reducer --> subscribe --> getState --> component
使用react-redux以后,流程:
component --> actionCreator(data) --> reducer --> component
store的三大功能:dispatch,subscribe,getState都不须要手动来写了。react-redux帮咱们作了这些,同时它提供了两个好基友Provider和connect。 Provider是一个组件,它接受store做为props,而后经过context往下传,这样react中任何组件均可以经过contex获取store。也就意味着咱们能够在任何一个组件里利用dispatch(action)来触发reducer改变state,并用subscribe监听state的变化,而后用getState获取变化后的值。可是并不推荐这样作,它会让数据流变的混乱,过分的耦合也会影响组件的复用,维护起来也更麻烦。 Connect --connect(mapStateToProps, mapDispatchToProps, mergeProps, options)是一个函数,它接受四个参数而且再返回一个函数--wrapWithConnect,wrapWithConnect接受一个组件做为参数wrapWithConnect(component),它内部定义一个新组件Connect(容器组件)并将传入的组件(ui组件)做为Connect的子组件而后return出去。
mapStateToProps(state, [ownProps]):
mapStateToProps 接受两个参数,store的state和自定义的props,并返回一个新的对象,这个对象会做为props的一部分传入ui组件。咱们能够根据组件所须要的数据自定义返回一个对象。ownProps的变化也会触发mapStateToProps
function mapStateToProps(state){
return {todos:state.todos};
}
复制代码
mapDispatchToProps(dispatch,[ownProps]):
mapDispatchToProps若是是对象,那么会和store绑定做为props的一部分传入ui组件。若是是个函数,它接受两个参数,bindActionCreators会将action和dispatch绑定并返回一个对象,这个对象会和ownProps一块儿做为props的一部分传入ui组件。因此不论mapDispatchToProps是对象仍是函数,它最终都会返回一个对象,若是是函数,这个对象的key值是能够自定义的.
function mapDispatchToProps(dispatch) {
return {
todoActions: bindActionCreators(todoActionCreators, dispatch),
counterActions: bindActionCreators(counterActionCreators, dispatch)
};
}
复制代码
mapDispatchToProps返回的对象其属性其实就是一个个actionCreator,由于已经和dispatch绑定,因此当调用actionCreator时会当即发送action,而不用手动dispatch。ownProps的变化也会触发mapDispatchToProps.
mergeProps(stateProps,dispatchProps,ownProps):
将mapStateToProps() 与 mapDispatchToProps()返回的对象和组件自身的props合并成新的props并传入组件。默认返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。
options:
pure = true 表示Connect容器组件将在shouldComponentUpdate中对store的state和ownProps进行浅对比,判断是否发生变化,优化性能。为false则不对比。
1、Provider组件接受redux的store做为props,而后经过context往下传。
2、connect函数在初始化的时候会将mapDispatchToProps对象绑定到store,若是mapDispatchToProps是函数则在Connect组件得到store后,根据传入的store.dispatch和action经过bindActionCreators进行绑定,再将返回的对象绑定到store,connect函数会返回一个wrapWithConnect函数,同时wrapWithConnect会被调用且传入一个ui组件,wrapWithConnect内部使用class Connect extends Component定义了一个Connect组件,传入的ui组件就是Connect的子组件,而后Connect组件会经过context得到store,并经过store.getState得到完整的state对象,将state传入mapStateToProps返回stateProps对象、mapDispatchToProps对象或mapDispatchToProps函数会返回一个dispatchProps对象,stateProps、dispatchProps以及Connect组件的props三者经过Object.assign(),或者mergeProps合并为props传入ui组件。而后在ComponentDidMount中调用store.subscribe,注册了一个回调函数handleChange监听state的变化。
3、此时ui组件就能够在props中找到actionCreator,当咱们调用actionCreator时会自动调用dispatch,在dispatch中会调用getState获取整个state,同时注册一个listener监听state的变化,store将得到的state和action传给combineReducers,combineReducers会将state依据state的key值分别传给子reducer,并将action传给所有子reducer,reducer会被依次执行进行action.type的判断,若是有则返回一个新的state,若是没有则返回默认。combineReducers再次将子reducer返回的单个state进行合并成一个新的完整的state。此时state发生了变化。Connect组件中调用的subscribe会监听到state发生了变化,而后调用handleChange函数,handleChange函数内部首先调用getState获取新的state值并对新旧两个state进行浅对比,若是相同直接return,若是不一样则调用mapStateToProps获取stateProps并将新旧两个stateProps进行浅对比,若是相同,直接return结束,不进行后续操做。若是不相同则调用this.setState()触发Connect组件的更新,传入ui组件,触发ui组件的更新,此时ui组件得到新的props,react --> redux --> react 的一次流程结束。
1、Provider组件接受redux的store做为props,而后经过context往下传。
2、connect函数收到Provider传出的store,而后接受三个参数mapStateToProps,mapDispatchToProps和组件,并将state和actionCreator以props传入组件,这时组件就能够调用actionCreator函数来触发reducer函数返回新的state,connect监听到state变化调用setState更新组件并将新的state传入组件。
connect能够写的很是简洁,mapStateToProps,mapDispatchToProps只不过是传入的回调函数,connect函数在必要的时候会调用它们,名字不是固定的,甚至能够不写名字。
connect(state => state,action)(Component);
复制代码
import React, {Component, PropTypes} from 'react';
import ReactDOM, {render} from 'react-dom';
import {Provider, connect} from 'react-redux';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import { Router, Route, Redirect, IndexRoute, browserHistory, hashHistory } from 'react-router';
复制代码
若是你以为这篇文章对你有帮助的话~~
doreen's notes star一下啦~谢谢😀