如何在Redux应用程序中dynamic加载用于代码拆分的reducer?
我正在迁移到Redux。
我的应用程序由很多部分(页面,组件)组成,所以我想创build许多reducer。 Redux示例显示我应该使用combineReducers()
来生成一个reducer。
另外据我所知Redux应用程序应该有一个商店,它是一旦应用程序启动时创build。 当商店正在创build时,我应该通过我的组合减速机。 如果应用程序不是太大,这是有道理的。
但是,如果我构build多个JavaScript包,该怎么办? 例如,每个应用程序页面都有自己的包。 我认为在这种情况下,联合减速机是不好的。 我查看了Redux的源代码,发现了replaceReducer()
函数。 这似乎是我想要的。
我可以为我的应用程序的每个部分创build组合的replaceReducer()
器,并在应用程序的各个部分之间移动时使用replaceReducer()
。
这是一个好方法吗?
这不是一个完整的答案,但应该帮助你开始。 请注意,我并没有扔掉旧的减速器 –我只是在组合列表中添加新的减速器 。 我看不出有什么理由把旧的reducer扔掉 – 即使在最大的应用程序中,你也不可能有成千上万个dynamic模块,这是你可能想要断开应用程序中一些reducer的地方。
reducers.js
import { combineReducers } from 'redux'; import users from './reducers/users'; import posts from './reducers/posts'; export default function createReducer(asyncReducers) { return combineReducers({ users, posts, ...asyncReducers }); }
store.js
import { createStore } from 'redux'; import createReducer from './reducers'; export default function configureStore(initialState) { const store = createStore(createReducer(), initialState); store.asyncReducers = {}; return store; } export function injectAsyncReducer(store, name, asyncReducer) { store.asyncReducers[name] = asyncReducer; store.replaceReducer(createReducer(store.asyncReducers)); }
routes.js
import { injectAsyncReducer } from './store'; // Assuming React Router here but the principle is the same // regardless of the library: make sure store is available // when you want to require.ensure() your reducer so you can call // injectAsyncReducer(store, name, reducer). function createRoutes(store) { // ... const CommentsRoute = { // ... getComponents(location, callback) { require.ensure([ './pages/Comments', './reducers/comments' ], function (require) { const Comments = require('./pages/Comments').default; const commentsReducer = require('./reducers/comments').default; injectAsyncReducer(store, 'comments', commentsReducer); callback(null, Comments); }) } }; // ... }
可能有更好的expression方式 – 我只是展示了这个想法。
这是我在当前的应用程序中实现它(基于由GitHub问题的Dan的代码!)
// Based on https://github.com/rackt/redux/issues/37#issue-85098222 class ReducerRegistry { constructor(initialReducers = {}) { this._reducers = {...initialReducers} this._emitChange = null } register(newReducers) { this._reducers = {...this._reducers, ...newReducers} if (this._emitChange != null) { this._emitChange(this.getReducers()) } } getReducers() { return {...this._reducers} } setChangeListener(listener) { if (this._emitChange != null) { throw new Error('Can only set the listener for a ReducerRegistry once.') } this._emitChange = listener } }
在引导应用程序时创build一个registry实例,传递将包含在入口包中的reducer:
// coreReducers is a {name: function} Object var coreReducers = require('./reducers/core') var reducerRegistry = new ReducerRegistry(coreReducers)
然后在configuration存储和路由时,使用一个函数,您可以给reducerregistry:
var routes = createRoutes(reducerRegistry) var store = createStore(reducerRegistry)
这些function如下所示:
function createRoutes(reducerRegistry) { return <Route path="/" component={App}> <Route path="core" component={Core}/> <Route path="async" getComponent={(location, cb) => { require.ensure([], require => { reducerRegistry.register({async: require('./reducers/async')}) cb(null, require('./screens/Async')) }) }}/> </Route> } function createStore(reducerRegistry) { var rootReducer = createReducer(reducerRegistry.getReducers()) var store = createStore(rootReducer) reducerRegistry.setChangeListener((reducers) => { store.replaceReducer(createReducer(reducers)) }) return store }
这是一个基本的实例,它是使用这个设置创build的,它的来源是:
- 例
- 资源
它还涵盖必要的configuration,以便为所有减速器启用热重新加载。
现在有一个模块可以将reduce注入到redux存储中。 它被称为Redux注射器。 https://github.com/randallknutson/redux-injector
以下是如何使用它:
-
不要组合减速器。 而是把它们放在一个(嵌套的)函数对象中,就像你通常一样,但没有把它们合并起来。
-
使用redux-injector中的createInjectStore代替redux中的createStore。
-
用injectReducer注入新的reducer。
这里是一个例子:
import { createInjectStore, injectReducer } from 'redux-injector'; const reducersObject = { router: routerReducerFunction, data: { user: userReducerFunction, auth: { loggedIn: loggedInReducerFunction, loggedOut: loggedOutReducerFunction }, info: infoReducerFunction } }; const initialState = {}; let store = createInjectStore( reducersObject, initialState ); // Now you can inject reducers anywhere in the tree. injectReducer('data.form', formReducerFunction);
完全披露:我是模块的创build者。
截至2017年10月:
-
Reedux
实现了Dan所build议的,而没有触及你的商店,你的项目或你的习惯
还有其他的库,但是它们可能有太多的依赖关系,less的例子,复杂的用法,与一些中间件不兼容或者需要你重写你的状态pipe理。 从Reedux的介绍页复制:
- 终极版模块
- 终极版模块build设者
- 终极版,堆
- paradux
- 终极版dynamic减速器
- 终极版注射器
- 终极版,DYNAMIX
这里是另一个代码分割和Redux商店的例子 ,在我看来非常简单和优雅。 我认为这对那些正在寻找工作解决scheme的人来说可能相当有用。
这个存储有点简单,它不会强制你在你的状态对象中有一个命名空间(reducer.name),当然可能会与名称有冲突,但是你可以通过为你的reducer创build一个命名约定来控制它应该没事。