Skip to content

Commit 2876cb9

Browse files
author
zhongzhenlong
committed
2345678
1 parent 9a61527 commit 2876cb9

File tree

14 files changed

+325
-15
lines changed

14 files changed

+325
-15
lines changed

doc/redux.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
```
2+
import React, {Component, PropTypes} from 'react';
3+
4+
import ReactDOM, {render} from 'react-dom';
5+
6+
import {Provider, connect} from 'react-redux';
7+
8+
import {createStore, combineReducers, applyMiddleware} from 'redux';
9+
10+
import { Router, Route, Redirect, IndexRoute, browserHistory, hashHistory } from 'react-router';
11+
```
12+
13+
14+
15+
redux主要由三部分组成:
16+
>store,reducer,action。
17+
18+
### **store**
19+
是一个对象,它有四个主要的方法:
20+
21+
**1、dispatch:**
22+
23+
> 用于action的分发——在createStore中可以用middleware中间件对dispatch进行改造,比如当action传入dispatch会立即触发reducer,有些时候我们不希望它立即触发,而是等待异步操作完成之后再触发,这时候用redux-thunk对dispatch进行改造,以前只能传入一个对象,改造完成后可以传入一个函数,在这个函数里我们手动dispatch一个action对象,这个过程是可控的,就实现了异步。
24+
25+
**2、subscribe:**
26+
27+
> 监听state的变化——这个函数在store调用dispatch时会注册一个listener监听state变化,当我们需要知道state是否变化时可以调用,它返回一个函数,调用这个返回的函数可以注销监听。
28+
29+
let unsubscribe = store.subscribe(() => {console.log('state发生了变化')})
30+
31+
**3、getState:**
32+
33+
> 获取store中的state——当我们用action触发reducer改变了state时,需要再拿到新的state里的数据,毕竟数据才是我们想要的。getState主要在两个地方需要用到,一是在dispatch拿到action后store需要用它来获取state里的数据,并把这个数据传给reducer,这个过程是自动执行的,二是在我们利用subscribe监听到state发生变化后调用它来获取新的state数据,如果做到这一步,说明我们已经成功了。
34+
35+
**4、replaceReducer:**
36+
37+
> 替换reducer,改变state修改的逻辑。
38+
39+
store可以通过createStore()方法创建,接受三个参数,经过combineReducers合并的reducer和state的初始状态以及改变dispatch的中间件,后两个参数并不是必须的。store的主要作用是将action和reducer联系起来并改变state。
40+
41+
### **action:**
42+
43+
>action是一个对象,其中type属性是必须的,同时可以传入一些数据。action可以用actionCreactor进行创造。dispatch就是把action对象发送出去。
44+
45+
### **reducer:**
46+
47+
>reducer是一个函数,它接受一个state和一个action,根据action的type返回一个新的state。根据业务逻辑可以分为很多个reducer,然后通过combineReducers将它们合并,state树中有很多对象,每个state对象对应一个reducer,state对象的名字可以在合并时定义。
48+
49+
像这个样子:
50+
```
51+
const reducer = combineReducers({
52+
53+
a: doSomethingWithA,
54+
55+
b: processB,
56+
57+
c: c
58+
59+
})
60+
```
61+
62+
**combineReducers:**
63+
64+
>其实它也是一个reducer,它接受整个state和一个action,然后将整个state拆分发送给对应的reducer进行处理,所有的reducer会收到相同的action,不过它们会根据action的type进行判断,有这个type就进行处理然后返回新的state,没有就返回默认值,然后这些分散的state又会整合在一起返回一个新的state树。
65+
66+
接下来分析一下整体的流程,首先调用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数据。
67+
68+
69+
## React-Redux
70+
71+
如果只使用redux,那么流程是这样的:
72+
73+
> component --> dispatch(action) --> reducer --> subscribe --> getState --> component
74+
75+
用了react-redux之后流程是这样的:
76+
77+
> component --> actionCreator(data) --> reducer --> component
78+
79+
store的三大功能:dispatch,subscribe,getState都不需要手动来写了。
80+
react-redux帮我们做了这些,同时它提供了两个好基友Provider和connect。
81+
82+
### **Provider**
83+
```javascript
84+
import { Provider } from 'react-redux'; // 引入 react-redux
85+
86+
// ……
87+
render(
88+
<Provider store={store}>
89+
<Sample />
90+
</Provider>,
91+
document.getElementById('app'),
92+
);
93+
```
94+
是一个组件,它接受store作为props,然后通过context往下传,这样react中任何组件都可以通过contex获取store。也就意味着我们可以在任何一个组件里利用dispatch(action)来触发reducer改变state,并用subscribe监听state的变化,然后用getState获取变化后的值。但是并不推荐这样做,它会让数据流变的混乱,过度的耦合也会影响组件的复用,维护起来也更麻烦。
95+
96+
### **connect**
97+
98+
**`connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])`**
99+
100+
是一个函数,它接受四个参数并且再返回一个函数--wrapWithConnect,wrapWithConnect接受一个组件作为参数wrapWithConnect(component),它内部定义一个新组件Connect(容器组件)并将传入的组件(ui组件)作为Connect的子组件然后return出去。
101+
102+
所以它的完整写法是这样的:connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(component)
103+
104+
```
105+
// 例子1
106+
const mapStateToProps = (state) => {
107+
return {
108+
number: state.changeNumber.number,
109+
showAlert: state.toggleAlert.showAlert,
110+
};
111+
};
112+
// 例子2
113+
const mapStateToProps = (state, ownProps) => {
114+
// state 是 {userList: [{id: 0, name: 'Jack'}, ...]}
115+
return {
116+
isMe: state.userList.includes({id: ownProps.userId})
117+
};
118+
}
119+
120+
/*
121+
在组件中通过this.props.incrementNum()方式来dispatch Action出去,通知Reducer修改state。
122+
如果你对Action比较熟悉的话,可能会疑惑,this.props.incrementNum()只是生成了一个Action,应该是写成:dispatch(this.props. incrementNum())才对吧?
123+
继续看下面介绍的function形式的mapDispatchToProps就能明白,其实dispatch已经被connect封装进去了,因此你不必手动写dispatch了。
124+
*/
125+
const mapDispatchToProps = {
126+
incrementNum: action.number.incrementNum,
127+
decrementNum: action.number.decrementNum,
128+
clearNum: action.number.clearNum,
129+
toggleAlert: action.alert.toggleAlert,
130+
};
131+
132+
// 例子2
133+
import { bindActionCreators } from 'redux';
134+
const mapDispatchToProps = (dispatch, ownProps) => {
135+
return {
136+
incrementNum: bindActionCreators(action.number.incrementNum, dispatch),
137+
decrementNum: bindActionCreators(action.number.decrementNum, dispatch),
138+
clearNum: bindActionCreators(action.number.clearNum, dispatch),
139+
toggleAlert: bindActionCreators(action.alert.toggleAlert, dispatch),
140+
};
141+
};
142+
143+
const mergeProps = (stateProps, dispatchProps, ownProps) => {
144+
return {
145+
...ownProps,
146+
...stateProps,
147+
incrementNum: dispatchProps.incrementNum, // 只输出incrementNum
148+
};
149+
};
150+
151+
export default connect(
152+
mapStateToProps,
153+
mapDispatchToProps,
154+
mergeProps,
155+
)(Sample);
156+
```
157+
158+
**`mapStateToProps(state, [ownProps])`**
159+
160+
>mapStateToProps 接受两个参数,store的state和自定义的props,并返回一个新的对象,
161+
这个对象会作为props的一部分传入ui组件。
162+
我们可以根据组件所需要的数据自定义返回一个对象。
163+
ownProps,看名字就知道是组件自己原始的props(即不包含connect后新增的props)
164+
165+
166+
**`mapDispatchToProps(dispatch, [ownProps])`**
167+
168+
> mapDispatchToProps如果是对象,那么会和store绑定作为props的一部分传入ui组件。
169+
如果是个函数,它接受两个参数,bindActionCreators会将action和dispatch绑定并返回一个对象,这个对象会和ownProps一起作为props的一部分传入ui组件。
170+
所以不论mapDispatchToProps是对象还是函数,它最终都会返回一个对象,如果是函数,这个对象的key值是可以自定义的
171+
第二个可选参数ownProps和mapStateToProps里作用是一样的,不赘述。
172+
173+
function mapDispatchToProps(dispatch) {
174+
175+
return {
176+
177+
todoActions: bindActionCreators(todoActionCreators, dispatch),
178+
179+
counterActions: bindActionCreators(counterActionCreators, dispatch)
180+
181+
};
182+
183+
}
184+
mapDispatchToProps返回的对象其属性其实就是一个个actionCreator,因为已经和dispatch绑定,所以当调用actionCreator时会立即发送action,而不用手动dispatch。ownProps的变化也会触发mapDispatchToProps。
185+
186+
**`mergeProps(stateProps, dispatchProps, ownProps)`**
187+
188+
> 将mapStateToProps() 与 mapDispatchToProps()返回的对象和组件自身的props合并成新的props并传入组件。
189+
默认返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。
190+
经过conncet的组件的props有3个来源:一是由mapStateToProps将state映射成的props,二是由mapDispatchToProps将Action creator映射成的props,三是组件自身的props。
191+
connect的第三个参数mergeProps也是一个function:`[mergeProps(stateProps, dispatchProps, ownProps): props]`
192+
参数分别对应了上述props的3个来源,作用是整合这些props。例如过滤掉不需要的props:
193+
194+
**options:**
195+
196+
> pure = true 表示Connect容器组件将在shouldComponentUpdate中对store的state和ownProps进行浅对比,判断是否发生变化,优化性能。为false则不对比。
197+
198+
其实connect函数并没有做什么,大部分的逻辑都是在它返回的wrapWithConnect函数内实现的,确切的说是在wrapWithConnect内定义的Connect组件里实现的。

doc/router.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
Route可以向绑定的组件传递7个属性
3+
4+
>children,history,location,params,route,routeParams,routes
5+
6+
每个属性都包涵路由的相关的信息。
7+
8+
children(以路由的包涵关系为区分的组件),
9+
location(包括地址,参数,地址切换方式,key值,hash值)。
10+
11+
react-router提供Link标签,这只是对a标签的封装,值得注意的是,点击链接进行的跳转并不是默认的方式,react-router阻止了a标签的默认行为并用pushState进行hash值的转变。切换页面的过程是在点击Link标签或者后退前进按钮时,会先发生url地址的转变,Router监听到地址的改变根据Route的path属性匹配到对应的组件,将state值改成对应的组件并调用setState触发render函数重新渲染dom。

src/actions/app.js

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/css/app.less

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ CSS Modules 允许使用:global(.className)的语法,声明一个全局规则
121121
display: flex;
122122
justify-content: space-between;
123123
height: 48px;
124-
background-color: #fff;
124+
background-color: @primary-color;
125125
padding: 0;
126126

127127
.rightWarpper {
@@ -166,3 +166,75 @@ CSS Modules 允许使用:global(.className)的语法,声明一个全局规则
166166
}
167167
}
168168

169+
170+
171+
:global {
172+
#nprogress {
173+
pointer-events: none;
174+
175+
.bar {
176+
background: @primary-color;
177+
position: fixed;
178+
z-index: 1024;
179+
top: 0;
180+
left: 0;
181+
right: 0;
182+
width: 100%;
183+
height: 2px;
184+
}
185+
186+
.peg {
187+
display: block;
188+
position: absolute;
189+
right: 0;
190+
width: 100px;
191+
height: 100%;
192+
box-shadow: 0 0 10px @primary-color,0 0 5px @primary-color;
193+
opacity: 1.0;
194+
transform: rotate(3deg) translate(0px,-4px);
195+
}
196+
197+
.spinner {
198+
display: block;
199+
position: fixed;
200+
z-index: 1031;
201+
top: 15px;
202+
right: 15px;
203+
}
204+
205+
.spinner-icon {
206+
width: 18px;
207+
height: 18px;
208+
box-sizing: border-box;
209+
border: solid 2px transparent;
210+
border-top-color: @primary-color;
211+
border-left-color: @primary-color;
212+
border-radius: 50%;
213+
214+
:local {
215+
animation: nprogress-spinner 400ms linear infinite;
216+
}
217+
}
218+
}
219+
220+
.nprogress-custom-parent {
221+
overflow: hidden;
222+
position: relative;
223+
224+
#nprogress {
225+
.bar,
226+
.spinner {
227+
position: absolute;
228+
}
229+
}
230+
}
231+
}
232+
@keyframes nprogress-spinner {
233+
0% {
234+
transform: rotate(0deg);
235+
}
236+
237+
100% {
238+
transform: rotate(360deg);
239+
}
240+
}

src/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { render } from 'react-dom'
33
import { Provider } from 'react-redux'
44
import { browserHistory } from 'react-router'
55
import { syncHistoryWithStore } from 'react-router-redux'
6-
import store from './store'
6+
import store from './redux'
77
import Routers from './router'
88

99
/*
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import { keyMirror } from '../utils'
1+
import { keyMirror } from '../../utils/index'
22

33
const appTypes = keyMirror({
44
CHANGE_THEME: null,
55
TOGGLE_SIDER_FOLD: null,
66
LOGOUT: null,
7+
SHOW_LOADING: null,
8+
HIDE_LOADING: null,
9+
SWITCH_LOADING_STATUS: null,
710
})
811

912
module.exports = { appTypes }

src/redux/actions/app.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { appTypes } from './actionTypes'
2+
3+
export default {
4+
changeTheme: () => ({ type: appTypes.CHANGE_THEME }),
5+
toggleSiderFold: () => ({ type: appTypes.TOGGLE_SIDER_FOLD }),
6+
logout: () => ({ type: appTypes.LOGOUT }),
7+
switchLoading: ({ payload }) => {
8+
if (payload && payload.status != null) {
9+
return { type: payload.status ? appTypes.SHOW_LOADING : appTypes.HIDE_LOADING }
10+
}
11+
return { type: appTypes.SWITCH_LOADING_STATUS }
12+
},
13+
}
File renamed without changes.

src/store/index.js renamed to src/redux/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import thunkMiddleware from 'redux-thunk'
44
import { browserHistory } from 'react-router'
55
import { createLogger } from 'redux-logger'
66
import { routerMiddleware, routerReducer } from 'react-router-redux'
7-
import initState from '../models'
8-
import reducers from '../reducers'
7+
import initState from './models'
8+
import reducers from './reducers'
99

1010

1111
/*

src/models/app.js renamed to src/redux/models/app.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const initState = {
88
siderFold: false, // 是否折叠左边栏
99
theme: 'light', // 'light','dark'
1010
user: {},
11+
loading: false,
1112
}
1213

1314
export default initState

0 commit comments

Comments
 (0)