Redux 애플리케이션 스토어를 분할하여 애플리케이션 성능을 향상시키는 방법

현재, 사용자가 우리 사이트를 방문할 때 가장 좋은 응용 프로그램 불러오는 시간을 얻기 위해서, 우리는 인터넷에서 전송되는 모든 바이트의 코드에 대해 의문을 제기했다.
사용자가 전자상거래 사이트의 홈 페이지(react &redux)에 접근하고 있다고 가정하십시오.최상의 효과time to interactive를 얻기 위해javascript 패키지는 홈 페이지의 접는 부분에 필요한 UI 구성 요소만 포함해야 합니다.우리는 이 페이지에 접근하기 전에 제품 목록이나 결산 코드를 불러와서는 안 된다.
이를 위해 다음을 수행할 수 있습니다.
  • 로드 지연 경로 - 각 라우팅의 UI 구성 요소를 원하는 대로 번들합니다.
  • 페이지 축소 아래의 구성 요소를 로드하는 데 지연됩니다.
  • 감속기는요?
    구성 요소와 달리 메인 패키지는 홈 페이지에 필요한 복원기만 있는 것이 아니라 모든 복원기를 가지고 있다.저희가 못하는 이유는요. - 
  • 최선의 실천은 redux 상태 트리의 평탄함을 유지하는 것이다 - 복원기 사이에 부자 관계가 없어서 코드 분할점을 만들 수 없습니다.
  • 구성 요소와 복원기의 모듈 의존 트리가 다르다.store.js -imports-> rootReducer.js -imports-> reducer.js(files) 따라서 저장된 데이터가 주 구성 요소나 수요에 따라 사용되더라도 상점의 의존 관계 트리는 응용 프로그램의 모든 축소기를 포함한다.
  • 구성 요소에서 어떤 데이터를 사용하는 지식이 업무 논리적이거나 적어도 정적 분석할 수 없는 -  mapStateToProps는 런타임 함수입니다.
  • Reduxstore API는 코드를 열어 사용할 수 없습니다. 저장소를 만들기 전에 모든 간소화 프로그램은 루트 Reducer의 일부분이어야 합니다.
    하지만 잠시만요. 개발 과정에서 Reducer 코드를 업데이트할 때마다 제 상점은 웹 팩의 열 모듈을 통해 업데이트합니다.이게 어떻게 된 일입니까?네, 이 때문에 루트 리덕터를 다시 만들고 store를 사용합니다.Reducer API를 대체합니다.그것은 단일 감속기를 전환하거나 새 감속기를 추가하는 것처럼 간단하지 않다.
  • 어떤 익숙하지 않은 개념을 만났는가?redux, 모듈, 웹 패키지를 기본적으로 이해하려면 아래의 링크와 설명을 참고하십시오.
  • Redux - 애플리케이션 상태를 관리하는 간단한 라이브러리 core concepts, with react
  • 모듈 -  Intro , es6 modules , dynamic import
  • 레지스트리 - moduleB 가져오기moduleA의 경우 moduleBmoduleA 의 의존 항목이고, moduleC 가져오기moduleB 의 경우 생성된 의존 트리는 -  moduleA -> moduleB -> moduleC . 웹 팩과 같은 귀속기는 이 의존 트리를 돌아다니며 코드 라이브러리를 귀속합니다.
  • 코드 분할 - 부모 모듈이 동적 가져오기 하위 모듈을 사용할 때, 웹 팩은 하위 모듈과 의존항을 서로 다른 생성 파일에 묶고, 가져오기 호출을 실행할 때 클라이언트가 이 파일을 불러옵니다.
    Webpack은 코드 라이브러리의 모듈을 훑어보고 브라우저에서 불러올 패키지를 생성합니다.

  • 이제 당신은 상술한 개념에 익숙해졌으니 우리 깊이 이해합시다.
    reactredux 응용 프로그램의 전형적인 구조를 봅시다 -

    // rootReducer.js
    export default combineReducers({
      home: homeReducer,
      productList: productListReducer
    });
    
    // store.js
    export default createStore(rootReducer/* , initialState, enhancer */);
    
    // Root.js
    import store from './store';
    import AppContainer from './AppContainer';
    
    export default function Root() {
      return (
        <Provider store={store}>
          <AppContainer />
        </Provider>
      );
    }
    
    먼저 rootReducer와redux 저장소를 만들고 루트 구성 요소를 가져옵니다.이것은 다음과 같은 의존 관계 트리를 생성할 것이다
    RootComponent.js
    |_store.js
    | |_rootReducer.js
    |   |_homeReducer.js
    |   |_productListReducer.js
    |_AppContainer.js
      |_App.js
      |_HomePageContainer.js
      | |_HomePage.js
      |_ProductListPageContainer.js
        |_ProductListPage.js
    
    우리의 목표는 store와 AppContainer의 의존 트리를 통합하는 것이다 -
    따라서 하나의 구성 요소가 코드로 분리될 때 웹팩은 이 구성 요소와 해당하는 축소기를 필요에 따라 블록에 묶는다.필요한 의존 관계 트리가 어떤 모습일지 봅시다. -

    RootComponent.js
    |_AppContainer.js
      |_App.js
      |_HomePageContainer.js
      | |_HomePage.js
      | |_homeReducer.js
      |_ProductListPageContainer.js
        |_ProductListPage.js
        |_productListReducer.js
    
    니가 관찰하면의존 관계 트리에 저장이 없다는 것을 알 수 있습니다!
    위의 종속성 트리에서
  • 가설ProductListPageContainerAppContainer에서 동적으로 가져옵니다.웹팩은 현재 주 블록이 아니라 수요에 따라 구축되고 있다.
  • 모든 감속기는 현재 수입되어 상점의 한 용기에 등록되어 있다.
  • 흥미롭다현재 용기는 데이터와 조작을 연결할 뿐만 아니라 복원 프로그램도 연결합니다.
    이제 이 점을 어떻게 실현할지 생각해 봅시다!
    Redux store는 productListReducerrootReducer 의 첫 번째 매개 변수로 기대합니다.이 제한이 생기면 우리는 두 가지 일이 필요해-
  • 생성createStore 전에 컨테이너에 이경관 바인딩
  • 의 모든 이경관의 정의를 저장한 다음 하나로 묶을 수 있는 고급 실체.
  • storeManager라는 고급 실체가 있다면 다음과 같은 API를 제공합니다
  • sm.registerReducers()
  • sm.createStore()
  • sm.refreshStore()
  • 다음은 재구성된 코드와 의존 트리rootReducer-
    // HomePageContainer.js
    import storeManager from 'react-store-manager';
    import homeReducer from './homeReducer';
    
    storeManager.registerReducers({ home: homeReducer });
    
    export default connect(/* mapStateToProps, mapDispatchToProps */)(HomePage);
    
    // ProductListPageContainer.js
    import storeManager from 'react-store-manager';
    import productListReducer from './productListReducer';
    
    storeManager.registerReducers({ productList: productListReducer });
    
    export default connect(/* mapStateToProps, mapDispatchToProps */)(ProductListPage);
    
    
    // AppContainer.js
    import storeManager from 'react-store-manager';
    
    const HomeRoute = Loadable({
      loader: import('./HomePageContainer'),
      loading: () => <div>Loading...</div>
    });
    
    const ProductListRoute = Loadable({
      loader: import('./ProductListPageContainer'),
      loading: () => <div>Loading...</div>
    });
    
    function AppContainer({login}) {
      return (
        <App login={login}>
          <Switch>
            <Route exact path="/" component={HomeRoute} />
            <Route exact path="/products" component={ProductListRoute} />
          </Switch>
        </App>
      );
    }
    
    export default connect(/* mapStateToProps, mapDispatchToProps */)(AppContainer);
    
    // Root.js
    import storeManager from 'react-store-manager';
    import AppContainer from './AppContainer';
    
    export default function Root() {
      return (
        <Provider store={storeManager.createStore(/* initialState, enhancer */)}>
          <AppContainer />
        </Provider>
      );
    }
    
    RootComponent를 설치할 때 Reducer를 등록하고 저장소를 만들면 됩니다.이제 필요한 의존 나무가 생겼어요.
    RootComponent.js
    |_AppContainer.js
      |_App.js
      |_HomePageContainer.js
      | |_HomePage.js
      | |_homeReducer.js
      |_ProductListPageContainer.js
        |_ProductListPage.js
        |_productListReducer.js
    
    이제 동적 가져오기 온디맨드 로딩 rootReducer 을 사용하면 storeManager 도 온디맨드 블록으로 이동합니다.
    만세!임무 완수?...거의.
    문제는 수요에 따라 블록을 불러올 때 - ProductListPageContainer 수요에 따라 블록에 존재하는 호출은store Manager에 복원기를 등록하지만 새로운 등록 복원기를 포함하는 새로운 productListReducer 리셋 저장소를 사용하지 않습니다.따라서 저장된 루트 Reducer를 업데이트하려면 Redux의 저장을 사용해야 합니다.Reducer API를 대체합니다.
    따라서 동적으로 하위 세대 (sm.registerReducers() 의 부모 세대 (rootReducer 를 불러올 때 AppContainer.js 호출만 실행합니다.따라서 ProductListPageContainer.js 데이터에 접근하거나 sm.refreshStore() 데이터 포인트에 대한 조작을 시작하기 전에 이 저장소는 productListReducer 를 가지고 있다.
    // AppContainer.js
    import {withRefreshedStore} from 'react-store-manager';
    
    const HomeRoute = Loadable({
      loader: withRefreshedStore(import('./HomePageContainer')),
      loading: () => <div>Loading...</div>
    });
    
    const ProductListRoute = Loadable({
      loader: withRefreshedStore(import('./ProductListPageContainer')),
      loading: () => <div>Loading...</div>
    });
    
    function AppContainer({login}) {
      return (
        <App login={login}>
          <Switch>
            <Route exact path="/" component={HomeRoute} />
            <Route exact path="/products" component={ProductListRoute} />
          </Switch>
        </App>
      );
    }
    
    우리는 ProductListPageContainer 어떻게 우리의 목표를 실현하는 데 도움을 줄 수 있는지 보았다.구현 -

    import { createStore, combineReducers } from 'redux';
    
    const reduceReducers = (reducers) => (state, action) =>
      reducers.reduce((result, reducer) => (
        reducer(result, action)
      ), state);
    
    export const storeManager = {
      store: null,
      reducerMap: {},
      registerReducers(reducerMap) {
        Object.entries(reducerMap).forEach(([name, reducer]) => {
          if (!this.reducerMap[name]) this.reducerMap[name] = [];
    
          this.reducerMap[name].push(reducer);
        });
      },
      createRootReducer() {
        return (
          combineReducers(Object.keys(this.reducerMap).reduce((result, key) => Object.assign(result, {
            [key]: reduceReducers(this.reducerMap[key]),
          }), {}))
        );
      },
      createStore(...args) {
        this.store = createStore(this.createRootReducer(), ...args);
    
        return this.store;
      },
      refreshStore() {
        this.store.replaceReducer(this.createRootReducer());
      },
    };
    
    export const withRefreshedStore = (importPromise) => (
      importPromise
        .then((module) => {
          storeManager.refreshStore();
          return module;
        },
        (error) => {
          throw error;
        })
    );
    
    export default storeManager;
    
    상술한 코드 세그먼트를 코드 라이브러리의 모듈로 사용할 수도 있고, 아래에 열거한 npm 패키지를 사용할 수도 있습니다 - 

    사지비나시 / redux 상점 매니저


    redux 메모리 관리자를 사용하여 redux 메모리를 성명적으로 분할하고 용기에 전체 redux 흐름을 가지도록 합니다


    redux 상점 매니저


    redux 메모리 관리자를 사용하여 redux 메모리를 성명적으로 분할하고 용기에 전체 redux 흐름을 가지도록 합니다

    설치


    실 추가 redux 상점 매니저

    문제

  • 전통적으로rootReducer는combinereducer를 사용하여 수동으로 만들었기 때문에 데이터를 사용하는 작은 위젯을 불러오는 방법을 기반으로 (주 패키지에서든 필요에 따라 패키지에서든) 코드를 분리하는 것이 어려워졌다.
  • Bundler cant tree shake 또는 dead code는 rootReducer를 제거하고 그 데이터가 용기 구성 요소에 사용되지 않도록 합니다
  • 솔루션

  • reducer와 트리거를 사용하여 저장된 데이터를 조작하는 용기는 reducer를 저장에 추가하는 것을 책임진다
    이것은 용기가 링크를 통해 전체redux 흐름을 가지게 한다

  • mapDispatchToProps를 통해 구성 요소 도구로 사용하는 동작

  • Reducer는 storeManager를 통해 데이터를 업데이트합니다.레지스트리 삭제기

  • mapStateToProps를 통해 데이터를 구성 요소 도구로 사용
  • 필요한 블록에 따라 저장소를 불러올 때 redux 저장소의 replaceReducer API를 사용하여 모든 복원 프로그램을 등록합니다. 저장소가 새로 고쳐집니다...
  • View on GitHub
    아직 개발되지 않은 구축 최적화 분야에 대한 인사:)
    이 콘셉트 좋아해요? - 글을 공유하고 git 환매에 별을 추가하십시오:)

    좋은 웹페이지 즐겨찾기