В наши дни для достижения оптимального времени загрузки приложений, когда пользователи посещают наш веб -сайт, мы ставим под сомнение каждый байт кода, который передается в сети.
Допустим, пользователь посещает домашнюю страницу веб-сайта электронной коммерции (React & Redux). Чтобы достичь лучшего Время до интерактивного , пакет JavaScript должен иметь только компоненты пользовательского интерфейса, необходимые для того, чтобы отображать более высокую часть домашней страницы. Мы не должны загружать код списка продуктов или заказа перед посещением этих страниц.
Чтобы достичь этого, вы можете:
- Ленивые маршруты нагрузки-компоненты каждого интерфейса пользовательского интерфейса в пакетах по требованию.
- ленивый загрузите компоненты ниже страницы.
А как насчет редукторов? В отличие от компонентов, главный пакет имеет все редукторы, а не только те, которые необходимы на домашней странице. Причины, по которым мы не могли это сделать, были-
- Лучшая практика состоит в том, чтобы сохранить плоское дерево Redux-никаких отношений между родителями и детьми между редакторами, чтобы создать точку сдвига кода.
- Деревья зависимости модуля компонентов и редукторов не одинаковы.
store.js -Imports-> rootreducer.js -Imports-> Reducer.js (файлы)Таким образом, дерево зависимости хранилища содержит все редукторы приложения, даже если хранимых данных используются основным компонентом или компонентом по требованию. - Знание того, какие данные используются в компоненте, является бизнес-логикой или, по крайней мере, не является статически анализируемым-
MapStateToPropsэто функция времени выполнения. - 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
Если вы соблюдаете. Вы заметите, что в дереве зависимости нет магазина!
В приведенном выше дереве зависимости
- Скажи
ProductListPageContainerдинамически импортируется вAppContainerАнкет WebPack теперь строитProductlistreducerв чанке по требованию, а не в главной части. - Каждый редуктор теперь импортируется и зарегистрирован в магазине в контейнере.
Интересно! Теперь контейнеры не только связывают данные и действия, но и редукторы.
Теперь давайте выясним, как это добиться!
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
Проблема
- RootReducer традиционно создается вручную с использованием Combinereducers, и это делает редукторы с рассылкой кода в зависимости от того, как усердно загружаются виджеты, потребляющие их данные (независимо от того, находятся ли они в основном пакете или на пучках по требованию).
- Bundler Cant Cant Tree Code или Dead Code устраняют корнеорусчик, чтобы не включать редукторы, данные которых не используются какими-либо компонентами контейнера
Решение
- Пусть контейнеры, которые собираются потреблять данные, хранящиеся с помощью редуктора и запуска, несут ответственность за добавление редуктора в хранилище, это делает контейнер, владеющий всем потоком Redux, связываясь
- Действия в качестве компонента реквизита через mapdispatchtoprops
- Reducer отвечает за обновление данных через StoreManager.RegisterReduers
- Данные в качестве компонента реквизита через MapStateToProps
- Используйте API API Redux Store Store, любые редукторы зарегистрированы, когда нагрузка по требованию по требованию, магазин обновляется …
Передай привет неиспользованной области оптимизации сборки:)
Нравится концепция? -Пожалуйста, поделитесь статьей и снимайте Git Repo:)
Оригинал: “https://dev.to/websavi/how-to-code-split-redux-store-to-further-improve-your-apps-performance-4gg9”