[javascript] Redux 애플리케이션에서 코드 분할을 위해 리듀서를 동적으로로드하는 방법은 무엇입니까?

Redux로 마이그레이션하겠습니다.

내 응용 프로그램은 많은 부분 (페이지, 구성 요소)으로 구성되어 있으므로 많은 감속기를 만들고 싶습니다. Redux 예제는 내가 사용해야한다는 것을 보여줍니다.combineReducers() 하나의 감속기를 생성 데 .

또한 Redux 응용 프로그램에는 하나의 저장소가 있어야하며 응용 프로그램이 시작되면 만들어집니다. 상점을 만들 때 결합 감속기를 통과해야합니다. 응용 프로그램이 너무 크지 않은 경우 이치에 맞습니다.

그러나 둘 이상의 JavaScript 번들을 빌드하면 어떻게됩니까? 예를 들어, 응용 프로그램의 각 페이지에는 고유 한 번들이 있습니다. 나는이 경우 하나의 결합 감속기가 좋지 않다고 생각합니다. 나는 Redux의 출처를 살펴 보았고replaceReducer() 기능 . 내가 원하는 것 같습니다.

응용 프로그램 및 사용 각 부분에 대해 결합 된 감속기를 만들 수 있습니다. replaceReducer() 사이를 이동할 때 수 있습니다.

이것이 좋은 접근입니까?



답변

업데이트 : 트위터가 어떻게하는지보십시오 .

이것은 정답은 아니지만 시작하는 데 도움이됩니다. 나는 오래된 감속기를 버리지 않고, 단지 새로운 감속기 를 조합 목록에 추가하고 있습니다. 이전 리듀서를 버릴 이유가 없습니다. 심지어 가장 큰 앱에서도 수천 개의 다이내믹 모듈이 없을 것입니다. 높습니다.이 경우 애플리케이션에서 일부 리듀서를 분리 .

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));
}

route.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);
      })
    }
  };

  // ...
}

이것을 표현하는 더 깔끔한 방법이있을 수 있습니다. 단지 아이디어를 보여주는 것입니다.


답변

이것이 내가 현재 앱에서 구현 한 방법입니다 (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
  }
}

앱을 부트 스트랩 할 때 항목 번들에 포함될 리듀서를 전달하여 레지스트리 인스턴스를 만듭니다.

// coreReducers is a {name: function} Object
var coreReducers = require('./reducers/core')
var reducerRegistry = new ReducerRegistry(coreReducers)

그런 다음 상점과 라우트를 구성 할 때 감속기 레지스트리를 제공 할 수있는 기능을 사용하십시오.

var routes = createRoutes(reducerRegistry)
var store = createStore(reducerRegistry)

이러한 기능은 다음과 같습니다.

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
}

다음은이 설정으로 만든 기본 라이브 예제와 그 소스입니다.

또한 모든 감속기에 핫 리로딩을 가능하게하는 데 필요한 구성을 다룹니다.


답변

이제 redux 저장소에 주입 감속기를 추가하는 모듈이 있습니다. 이를 Redux Injector 라고 합니다.

사용 방법은 다음과 같습니다.

  1. 감속기를 함께 사용하지 마십시오. 대신 평소와 같이 기능을 결합하지 않고 (중첩 된) 함수 객체에 넣으십시오.

  2. redux의 createStore 대신 redux-injector의 createInjectStore를 사용하십시오.

  3. injectReducer로 새로운 감속기를 주입하십시오.

예를 들면 다음과 같습니다.

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);

전체 공개 : 저는 모듈을 만든 사람입니다.


답변

2017 년 10 월 기준 :

  • 리드

    상점, 프로젝트 또는 습관을 건드리지 않고 Dan이 제안한 것을 구현합니다.

다른 라이브러리도 있지만 종속성이 너무 많거나 예제가 적고 복잡한 사용법이 있거나 일부 미들웨어와 호환되지 않거나 상태 관리를 다시 작성해야합니다. Reedux의 소개 페이지에서 복사 :


답변

Redux 앱을 변조하고 리듀서 및 미들웨어를 동적으로 추가 / 제거 할 수있는 새로운 라이브러리를 출시했습니다.

https://github.com/Microsoft/redux-dynamic-modules
참조하십시오

모듈은 다음과 같은 이점을 제공합니다.

  • 모듈은 응용 프로그램 전체 또는 여러 유사한 응용 프로그램간에 쉽게 재사용 할 수 있습니다.

  • 컴포넌트는 필요한 모듈을 선언하고 redux-dynamic-modules는 모듈이 컴포넌트에로드되도록합니다.

  • 상점에서 모듈을 동적으로 추가 / 제거 할 수 있습니다 (예 : 구성 요소가 마운트되거나 사용자가 작업을 수행 할 때

풍모

  • 리듀서, 미들웨어 및 상태를 재사용 가능한 단일 모듈로 그룹화하십시오.
  • 언제든지 Redux 스토어에서 모듈을 추가하고 제거하십시오.
  • 포함 된 구성 요소를 사용하여 구성 요소가 렌더링 될 때 자동으로 모듈을 추가
  • 확장 기능은 redux-saga 및 redux-observable을 포함하여 널리 사용되는 라이브러리와의 통합을 제공합니다

시나리오 예

  • 모든 리듀서에 대한 코드를 미리로드하지 않으려 고합니다. 일부 리듀서를위한 모듈을 정의하고 DynamicModuleLoader 및 react-loadable과 같은 라이브러리를 사용하여 런타임에 모듈을 다운로드하고 추가하십시오.
  • 애플리케이션의 다른 영역에서 재사용해야하는 일반적인 리듀서 / 미들웨어가 있습니다. 모듈을 정의하고 해당 영역에 쉽게 포함시킵니다.
  • 유사한 상태를 공유하는 여러 응용 프로그램이 포함 된 모노 리포지토리가 있습니다. 일부 모듈이 포함 된 패키지를 작성하고 애플리케이션 전체에서 재사용

답변

여기 에 코드 분할 및 redux 상점 이있는 또 다른 예가 있습니다. 나는 그것이 효과적인 해결책을 찾고있는 사람들에게 매우 유용 할 것이라고 생각합니다.

저장소 는 약간 단순화되어 상태 객체에 네임 스페이스 (reducer.name)를 강요하지는 않지만 이름과 충돌 할 수 있지만 감속기와 이름 지정 규칙을 작성하여이를 제어 할 수 있습니다 괜찮을거야.


답변