Рубрики
Без рубрики

Как кодировать Split Redux Store, чтобы повысить производительность вашего приложения

Используя библиотеку Redux-Store-Manager, мы загружаем Redux Reducers по требованию и улучшаем производительность загрузки приложений React. Tagged with React, Redux, JavaScript, Performance.

В наши дни для достижения оптимального времени загрузки приложений, когда пользователи посещают наш веб -сайт, мы ставим под сомнение каждый байт кода, который передается в сети.

Допустим, пользователь посещает домашнюю страницу веб-сайта электронной коммерции (React & Redux). Чтобы достичь лучшего Время до интерактивного , пакет JavaScript должен иметь только компоненты пользовательского интерфейса, необходимые для того, чтобы отображать более высокую часть домашней страницы. Мы не должны загружать код списка продуктов или заказа перед посещением этих страниц.

Чтобы достичь этого, вы можете:

  1. Ленивые маршруты нагрузки-компоненты каждого интерфейса пользовательского интерфейса в пакетах по требованию.
  2. ленивый загрузите компоненты ниже страницы.

А как насчет редукторов? В отличие от компонентов, главный пакет имеет все редукторы, а не только те, которые необходимы на домашней странице. Причины, по которым мы не могли это сделать, были-

  1. Лучшая практика состоит в том, чтобы сохранить плоское дерево Redux-никаких отношений между родителями и детьми между редакторами, чтобы создать точку сдвига кода.
  2. Деревья зависимости модуля компонентов и редукторов не одинаковы. store.js -Imports-> rootreducer.js -Imports-> Reducer.js (файлы) Таким образом, дерево зависимости хранилища содержит все редукторы приложения, даже если хранимых данных используются основным компонентом или компонентом по требованию.
  3. Знание того, какие данные используются в компоненте, является бизнес-логикой или, по крайней мере, не является статически анализируемым- MapStateToProps это функция времени выполнения.
  4. Redux Store API не поддерживает выброс кода из коробки, и все редукторы должны быть частью корневой ставки перед созданием магазина. Но подождите, во время разработки, всякий раз, когда я обновляю свой код редуктора, мой магазин обновляется через Замена горячего модуля WebPack Анкет Как это работает? Да, для этого мы воссоздаем RootReducer и используем Store.Replacerucer API Анкет Это не так просто, как переключение одного редуктора или добавление нового.

Столкнулся с любыми незнакомыми понятиями? Пожалуйста, обратитесь к ссылкам и описанию ниже, чтобы получить базовое понимание Redux, модулей и Webpack.

  • Redux-простая библиотека для управления состоянием приложения, Основные понятия , с React Анкет
  • Модули- Вступление , модули ES6 , динамический импорт
  • Дерево зависимости-если moduleb импортируется в модуль , когда модуль зависимость от модули и если modulec импортируется в Moduleb , тогда результирующее дерево зависимости- modulea -> moduleb -> modulec Анкет Бундлеры, такие как WebPack, пройдут это дерево зависимостей, чтобы объединить кодовую базу.
  • Spectation-когда родительский модуль импортирует дочерний модуль, используя динамический импорт, WebPack объединяет дочерний модуль и его зависимости в другом файле сборки, который будет загружен клиентом, когда импортный вызов выполняется во время выполнения. WebPack проходит модули в кодовой базе и генерирует пакеты, которые будут загружены браузером.

Теперь вы знакомы с вышеупомянутыми понятиями, давайте погрузимся.

Давайте посмотрим на типичную структуру приложения React-Redux-

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

Сначала вы создаете RootReducer и Redux Store, а затем импортируйте магазин в корневой компонент. Это приводит к дереву зависимостей, как показано ниже

RootComponent.js
|_store.js
| |_rootReducer.js
|   |_homeReducer.js
|   |_productListReducer.js
|_AppContainer.js
  |_App.js
  |_HomePageContainer.js
  | |_HomePage.js
  |_ProductListPageContainer.js
    |_ProductListPage.js

Наша цель – объединить деревья зависимости магазина и AppContainer – Таким образом, когда компонент рассыпается кодом, WebPack объединяет этот компонент и соответствующий редуктор в чанке по требованию. Посмотрим, как может выглядеть желаемое дерево зависимостей-

RootComponent.js
|_AppContainer.js
  |_App.js
  |_HomePageContainer.js
  | |_HomePage.js
  | |_homeReducer.js
  |_ProductListPageContainer.js
    |_ProductListPage.js
    |_productListReducer.js

Если вы соблюдаете. Вы заметите, что в дереве зависимости нет магазина!

В приведенном выше дереве зависимости

  1. Скажи ProductListPageContainer динамически импортируется в AppContainer Анкет WebPack теперь строит Productlistreducer в чанке по требованию, а не в главной части.
  2. Каждый редуктор теперь импортируется и зарегистрирован в магазине в контейнере.

Интересно! Теперь контейнеры не только связывают данные и действия, но и редукторы.

Теперь давайте выясним, как это добиться!

Redux Store ожидает rootreducer Как первый аргумент Createstore . С этим ограничением нам нужно две вещи –

  • Сделать контейнеры связывать редукторы перед созданием rootreducer
  • Сущность высшего порядка, которая может удерживать определения всех редукторов, которые присутствуют в rootreducer прежде чем они упакованы в один.

Итак, допустим, у нас есть сущность высшего порядка под названием StoreManager который обеспечивает следующие API

  • sm.registerreducers ()
  • sm.createstore ()
  • sm.refreshstore ()

Ниже приведена рефакторированный код и дерево зависимостей с StoreManager

// 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: () => 
Loading...
}); const ProductListRoute = Loadable({ loader: import('./ProductListPageContainer'), loading: () =>
Loading...
}); function AppContainer({login}) { return ( ); } export default connect(/* mapStateToProps, mapDispatchToProps */)(AppContainer); // Root.js import storeManager from 'react-store-manager'; import AppContainer from './AppContainer'; export default function Root() { return ( ); }

Рудовые только зарегистрированы, а хранилище создается при монтировании RootComponent. Теперь у этого есть желаемое дерево зависимостей

RootComponent.js
|_AppContainer.js
  |_App.js
  |_HomePageContainer.js
  | |_HomePage.js
  | |_homeReducer.js
  |_ProductListPageContainer.js
    |_ProductListPage.js
    |_productListReducer.js

Теперь если ProductListPageContainer По требованию загружен с использованием динамического импорта, Productlistreducer также перемещается в кусок по требованию.

Ура! Миссия выполнена?… Почти

Проблема в том, что когда загружается кусок по требованию- sm.registerreducers () Вызовы, присутствующие в чанке по требованию, зарегистрируйте редукторы на StoreManager, но не обновляйте магазин Redux с новым rootreducer содержащий вновь зарегистрированные редукторы. Таким образом, чтобы обновить rootreducer магазина, нам нужно использовать Redux’s Store.Replacerucer API Анкет

Поэтому, когда родитель ( appcontainer.js ), который динамически загружает ребенка ( productlistpageContainer.js ), он просто должен сделать sm.refreshstore () вызов. Так что в магазине есть Productlistreducer , до ProductListPageContainer может начать доступ к данным или запускам действий на, ProductList DataPoint.

// AppContainer.js
import {withRefreshedStore} from 'react-store-manager';

const HomeRoute = Loadable({
  loader: withRefreshedStore(import('./HomePageContainer')),
  loading: () => 
Loading...
}); const ProductListRoute = Loadable({ loader: withRefreshedStore(import('./ProductListPageContainer')), loading: () =>
Loading...
}); function AppContainer({login}) { return ( ); }

Мы видели, как StoreManager Помогает достичь наших целей. Давайте реализуем это-

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, указанный ниже-

Sagiavinash/Redux Store-Manager

Декларативно-код спредит свой магазин Redux и сделайте контейнеры с собственным потоком Redux с помощью Redux Store-Manager

Декларативно-код спредит свой магазин Redux и сделайте контейнеры с собственным потоком Redux с помощью Redux Store-Manager

Монтаж

yarn add redux-store-manager

Проблема

  1. RootReducer традиционно создается вручную с использованием Combinereducers, и это делает редукторы с рассылкой кода в зависимости от того, как усердно загружаются виджеты, потребляющие их данные (независимо от того, находятся ли они в основном пакете или на пучках по требованию).
  2. Bundler Cant Cant Tree Code или Dead Code устраняют корнеорусчик, чтобы не включать редукторы, данные которых не используются какими-либо компонентами контейнера

Решение

  1. Пусть контейнеры, которые собираются потреблять данные, хранящиеся с помощью редуктора и запуска, несут ответственность за добавление редуктора в хранилище, это делает контейнер, владеющий всем потоком Redux, связываясь
    • Действия в качестве компонента реквизита через mapdispatchtoprops
    • Reducer отвечает за обновление данных через StoreManager.RegisterReduers
    • Данные в качестве компонента реквизита через MapStateToProps
  2. Используйте API API Redux Store Store, любые редукторы зарегистрированы, когда нагрузка по требованию по требованию, магазин обновляется …

Передай привет неиспользованной области оптимизации сборки:)

Нравится концепция? -Пожалуйста, поделитесь статьей и снимайте Git Repo:)

Оригинал: “https://dev.to/websavi/how-to-code-split-redux-store-to-further-improve-your-apps-performance-4gg9”