所用版本:
- react 15.3.2
- react-router 2.8.1
创建src/store
import { createStore, applyMiddleware, combineReducers } from 'redux';
import thunkMiddleware from 'redux-thunk';
import promiseMiddleware from './common/middlewares/promiseMiddleware';
import apiMiddleware from './common/middlewares/api';
let ReduxLogger = require('ReduxLogger');
// 根据不同环境加载所需的 middlewares
let middlewares;
let initialState;
if (process.env.NODE_ENV === 'production') {
middlewares = [
thunkMiddleware,
apiMiddleware,
promiseMiddleware
];
initialState = {};
} else {
const logger = ReduxLogger.createLogger({
collapsed: true
});
middlewares = [
thunkMiddleware,
apiMiddleware,
promiseMiddleware,
logger
];
initialState = window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__();
}
// 定义将始终存在于应用程序中的 Reducer
// 注意这里的reducer需要引入
const staticReducers = {
common: commonReducer,
app: appReducer
}
function createReducer(asyncReducers) {
return combineReducers({
...staticReducers,
...asyncReducers
})
}
// 创建并配置store
function configureStore(initialState) {
const store = createStore(createReducer(), initialState, applyMiddleware(...middlewares));
// 添加一个对象,跟踪已注册的异步 Reducer
store.asyncReducers = {};
return store;
}
const store = configureStore(initialState);
// 关键:注入 reducer 函数
// 此函数添加 async reducer,并创建一个新的组合 reducer
export const injectAsyncReducer = (store, name, asyncReducer) => {
if(typeof name === 'string' && typeof asyncReducer === 'function') {
store.asyncReducers[name] = asyncReducer;
}
if(Object.prototype.toString.call(name) === "[object Object]") {
store.asyncReducers = name;
}
// 用新的store取代之前的
store.replaceReducer(createReducer(store.asyncReducers));
};
export default store;
现在调用injectAsyncReducer函数就可以注入其他reducer,配合路由:在每个界面下新建单独的route/index和reducer/index来进行管理。
界面reducer/index
根据情况将界面内reducer拆分,然后用combineReducers组合起来
import {combineReducers} from 'redux';
import scroll from './scroll';
import result from './result';
const moduleReducer = combineReducers({
scroll,
result
});
export default moduleReducer;
界面route/index
import store, { injectAsyncReducer } from '../../../store';
module.exports = {
path: '路由地址/和之前的路由写法一样',
getComponent(nextState, cb) {
require.ensure([], require => {
const reducer = require('../reducers/index');
injectAsyncReducer(store, 'reducer', reducer);
cb(null, require('../index.js'));
}, 'webpackChunkName');
}
};
getComponent 对应 <Route path="/path" component="component"/> 中的 component 属性,只不过 getComponent 是异步的,当路由匹配时,才会调用这个方法。
如果需要返回多个子组件,可以用 getComponents 的方法,将多个子组件作为对象的属性通过 callback 的形式返回即可。
require.ensure
require.ensure(dependencies, callback, chunkName)
这是webpack提供的方法,也是按需加载的核心方法,第一个参数是依赖,第二个是回调,第三个是打包时的chunkName,用来指定chunk file的名字。
创建route
import React, {Component} from 'react';
import { Provider } from 'react-redux';
import { Router, Route, IndexRoute, useRouterHistory } from 'react-router';
import { createHashHistory } from 'history';
import store from './store';
import App from './modules/app';
import NoMatch from './modules/app/NoMatch';
const routes = {
path: '/',
// App中挂载了一些全局组件,比如Loading,Toast,Confirm,通过this.props.children来装载子组件
component: App,
indexRoute: {
// NoMatch 组件无内容,只有跳转到首页的功能
component: NoMatch
},
getChildRoutes(nextState, cb) {
require.ensure([], require => {
cb(null, [
// 加载界面路由
require('./pagePath/routes/index'),
// 其他界面
...
])
}, 'dynamicRoutes');
}
};
const history = useRouterHistory(createHashHistory)({
queryKey: false
});
export default class Root extends Component {
render() {
return (
<Provider store={store}>
<Router history={history} routes={routes}></Router>
</Provider>
);
}
}
getChildRoutes 和对象配置路由中的 childRoutes 相同,但是异步并且接收partialNextState
挂载
import Root from './routes';
...
render(<Root />, document.getElementById('app'));
react router 4+
以下是为了配合 react16+ 和 react-router-dom4+ 版本进行的改造
版本4以上的路由,在路由写法上跟之前有些不同,需要采用标签的形式,配合 react 中的 lazy 函数。 需要注意的是,lazy 函数引入的组件必须用 Suspense 来包裹。
import React, { Suspense, lazy } from "react";
import { HashRouter, Route, Switch } from "react-router-dom";
import TrainerHome from "../pages/trainerHome/route"
const MyInformation = lazy(() => import(/* webpackChunkName: "MyInformation" */ "../pages/MyInformation"))
export default <Suspense fallback={""}>
<HashRouter>
<Switch>
<Route exact path="/info" component={MyInformation} />
<Route exact path="/" component={MyInformation} />
</Switch>
</HashRouter>
</Suspense>
不需要注入 redux 的界面,直接用 lazy 来引入即可,需要注入的界面,其关键还是上面的 injectAsyncReducer 函数。
import store, { injectAsyncReducer } from '../../../store/index';
import {lazy} from "react";
import reducer from "../reducers/result"
// 注入reducer
injectAsyncReducer(store, 'trainerData', reducer);
export default lazy(() => import(/* webpackChunkName: "TrainerHome" */ "../index"))