初步目录结构
整体目录结构
src目录结构
entry 存放页面的入口文件
src 页面的源文件
static 页面源文件打包后生成的文件
webpack webpack打包文件
package.json package.json文件
.babelrc 支持es6语法
其中 src
中子目录结构如下:
components
页面组件
constants
页面需要用到的一些常量
helpers
工具方法
mock
模拟json数据
redux
redux数据控制目录
views
页面视图文件,主要视图全在这个文件
react配合redux编写页面流程
entry/hello.js
import React from 'react';import { render } from 'react-dom';import HelloPage from '../src/views/HelloPage';import helloStore from '../src/redux/stores/helloStore';import { Provider } from 'react-redux';render( <Provider store={helloStore}> <HelloPage /> </Provider>, document.getElementById('hello'));
HelloPage.jsx
是视图文件,通过react-redux
中的Provider
将store
绑定到视图中
src/redux/actions/helloAction.js
import fetch from 'isomorphic-fetch';export const ADD_COUNT = 'ADD_COUNT';export const ADD_PERSON = 'ADD_PERSON';export const DELETE_PERSON = 'DELETE_PERSON';export const FETCH_START = 'FETCH_START';export const FETCH_SUCCESS = 'FETCH_SUCCESS';export const FETCH_FAUILE = 'FETCH_FAUILE';export function addCount() { return { type : ADD_COUNT }}export function addPerson(person) { return { type : ADD_PERSON, person }}export function deletePerson(idx) { return { type : DELETE_PERSON, idx }}export function refreshStart() { return { type : FETCH_START }}export function refreshSuccess(list) { return { type : FETCH_SUCCESS, list }} export function refreshFauile() { return { type : FETCH_FAUILE }}export function refresh() { return dispatch => { dispatch(refreshStart()); return fetch(`src/mock/fetch-data-mock.json`) .then(response => response.json()) .then(json => { setTimeout(() => { dispatch(refreshSuccess(json && json.data.list)); }, 3000); }); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
action
中主要定义事件类型,基本上都是一些常量。另外如果要进行一些非常量返回,比如异步请求,则需要输出一个函数,这个函数通常带有dispatch
这个对象,用于对action
的重新包装,其实类似于后台语言中的“拦截器”,返回函数之后,需要在store
中配置thunkMiddleware
。
src/redux/reducers/helloReducers.js
import { combineReducers } from 'redux';import { ADD_COUNT, ADD_PERSON, DELETE_PERSON, FETCH_START, FETCH_SUCCESS, FETCH_FAUILE } from '../actions/helloActions';const initialState = { count : 0, loaded : false, personList : [ {"name" : "lily", "age" : 21} ]};function count(state = initialState.count, action) { switch (action.type) { case ADD_COUNT : return state + 1; default : return state; }}function personList(state = initialState, action) { switch (action.type) { case ADD_PERSON : return Object.assign({}, ...state, { personList : [...state.personList, action.person] }); case DELETE_PERSON : return Object.assign({}, ...state, { personList : state.personList.filter((s, i) => { return action.idx !== i; }) }); case FETCH_START : case FETCH_SUCCESS : case FETCH_FAUILE : return fetchDataFromServer(state, action); default : return state; }}function fetchDataFromServer(state, action) { if (action.type === FETCH_SUCCESS) { console.log(action); return Object.assign({}, ...state, { personList : [...state.personList, ...action.list], loaded : true }); } return state;}const helloReducers = combineReducers({ count, personList});export default helloReducers;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
reducer
中是对action
发起的数据进行处理,这其中可能action只是发出了一个纯指令,带参数或者不带参数根据业务的需求来定,总一个准则就是,初始的state经过处理之后会返回新的state。即:( state, action ) => newState
。同时另外注意一点的是,初始的state值是不会变得,需要操作则会另外创建一个新的state,来保证某些场景的性能问题(因为状态的改变会导致页面重新渲染,如果返回的state引用相同,则不会存在如此问题)。可以使用Immutable.js来保证state的纯洁性。
src/redux/stores/helloStore.js
import { createStore, applyMiddleware } from 'redux';import helloReducers from '../reducers/helloReducers';import logger from '../middlewares/loggerMiddleWare';import thunkMiddleware from 'redux-thunk';let createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);export default createStoreWithMiddleware(helloReducers);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
store
中主要绑定从reducer
返回的状态
src/views/HelloPage.jsx
import React from 'react';import { connect } from 'react-redux';import { addCount, addPerson, deletePerson, refresh } from '../redux/actions/helloActions';import HelloWorld from '../components/hello/HelloWorld';import Button from 'react-bootstrap-myui/lib/Button';import Table from 'react-bootstrap-myui/lib/Table';const HelloPage = React.createClass({ render () { let { count, list, loaded } = this.props; let personTbody = list.map((l, i) => (<tr key={"tr"+i}><td>{i}</td><td>{l.name}</td> <td>{l.age}</td><td><a href="javascript:;" onClick={this.deletePerson.bind(this, i)}>delete</a></td></tr>)); return ( <div id="reactPage"> <HelloWorld /> <Button onClick={this.addperson}>add person</Button> <Button onClick={this.refresh}>refresh</Button> {loaded ? (<span>加载完成</span>) : (<span>正在加载数据...</span>)} <Table striped hover> <thead> <tr> <th>#</th> <th>name</th> <th>age</th> <th>action</th> </tr> </thead> <tbody> {personTbody} </tbody> </Table> <span>show me the current count : {count || 0}</span> <div><button onClick={this.addCount}>Add</button></div> </div> ); }, addCount() { let { dispatch, count } = this.props; dispatch(addCount()); }, addperson() { let { dispatch } = this.props; dispatch(addPerson({"name" : "tome", age : 25})); }, deletePerson(index) { let { dispatch } = this.props; dispatch(deletePerson(index)); }, refresh() { let { dispatch } = this.props; dispatch(refresh()); }});function select(state) { return { count : state.count, list : state.personList.personList, loaded : state.personList.loaded }}export default connect(select)(HelloPage);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
详细的页面,在页面中默认会带有从store
中返回的状态,会以props
的形式传递到页面中,默认会带有一个dispatch
函数,用于发起action
,因此这个action
可以绑定页面的一些业务逻辑操作,比如:“新增”、“删除”、“修改”……