[Maps] Code split Maps app (#64594)
Code-split the maps-plugin to reduce the initial `maps.plugin.js` size. There were two main code dependencies in the plugin initialization that were the root cause of the large bundle size. - `GisMap` wraps the entire application UX, including the add-layer-wizard. The layer wizards only need to be available there. This PR moves the `load_layer_wizard` dependency from the plugin-initialization to the `GisMap` component. - The `MapEmbeddableFactory` needs to be registered at plugin-initialization. However, this module imports a lot of core-application code. By code-splitting here, we avoid pulling in the entire Maps-app in the main bundle. This also introduces a lazy-initialization of the `GisMap` itself as an additional split to further reduce size of the bundles.
This commit is contained in:
parent
f126e6130b
commit
7bf7174a2d
|
@ -9,7 +9,11 @@ import { Filter } from 'src/plugins/data/public';
|
|||
|
||||
import { RenderToolTipContent } from '../../layers/tooltips/tooltip_property';
|
||||
|
||||
export const GisMap: React.ComponentType<{
|
||||
declare const GisMap: React.ComponentType<{
|
||||
addFilters: ((filters: Filter[]) => void) | null;
|
||||
renderTooltipContent?: RenderToolTipContent;
|
||||
}>;
|
||||
|
||||
export { GisMap };
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default GisMap;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { GisMap } from './view';
|
||||
import { GisMap as UnconnectedGisMap } from './view';
|
||||
import { exitFullScreen } from '../../actions/ui_actions';
|
||||
import { getFlyoutDisplay, getIsFullScreen } from '../../selectors/ui_selectors';
|
||||
import { triggerRefreshTimer, cancelAllInFlightRequests } from '../../actions/map_actions';
|
||||
|
@ -42,5 +42,6 @@ function mapDispatchToProps(dispatch) {
|
|||
};
|
||||
}
|
||||
|
||||
const connectedGisMap = connect(mapStateToProps, mapDispatchToProps)(GisMap);
|
||||
export { connectedGisMap as GisMap };
|
||||
const connectedGisMap = connect(mapStateToProps, mapDispatchToProps)(UnconnectedGisMap);
|
||||
export { connectedGisMap as GisMap }; // GisMap is pulled in by name by the Maps-app itself
|
||||
export default connectedGisMap; //lazy-loading in the embeddable requires default export
|
||||
|
|
|
@ -22,6 +22,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import uuid from 'uuid/v4';
|
||||
import { FLYOUT_STATE } from '../../reducers/ui';
|
||||
import { MapSettingsPanel } from '../map_settings_panel';
|
||||
import { registerLayerWizards } from '../../layers/load_layer_wizards';
|
||||
|
||||
const RENDER_COMPLETE_EVENT = 'renderComplete';
|
||||
|
||||
|
@ -36,6 +37,7 @@ export class GisMap extends Component {
|
|||
this._isMounted = true;
|
||||
this._isInitalLoadRenderTimerStarted = false;
|
||||
this._setRefreshTimer();
|
||||
registerLayerWizards();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
|
|
|
@ -40,7 +40,6 @@ function mapStateToProps(state = {}) {
|
|||
scrollZoom: getScrollZoom(state),
|
||||
disableInteractive: isInteractiveDisabled(state),
|
||||
disableTooltipControl: isTooltipControlDisabled(state),
|
||||
disableTooltipControl: isTooltipControlDisabled(state),
|
||||
hideViewControl: isViewControlHidden(state),
|
||||
};
|
||||
}
|
||||
|
|
18
x-pack/plugins/maps/public/embeddable/lazy/index.ts
Normal file
18
x-pack/plugins/maps/public/embeddable/lazy/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
// These are map-dependencies of the embeddable.
|
||||
// By lazy-loading these, the Maps-app can register the embeddable when the plugin mounts, without actually pulling all the code.
|
||||
|
||||
// @ts-ignore
|
||||
export * from '../../angular/services/gis_map_saved_object_loader';
|
||||
export * from '../map_embeddable';
|
||||
export * from '../../kibana_services';
|
||||
export * from '../../reducers/store';
|
||||
export * from '../../actions/map_actions';
|
||||
export * from '../../selectors/map_selectors';
|
||||
export * from '../../angular/get_initial_layers';
|
||||
export * from './../merge_input_with_saved_map';
|
|
@ -5,16 +5,16 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import React, { Suspense, lazy } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Unsubscribe } from 'redux';
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import {
|
||||
Embeddable,
|
||||
IContainer,
|
||||
EmbeddableInput,
|
||||
EmbeddableOutput,
|
||||
} from '../../../../../src/plugins/embeddable/public';
|
||||
import { APPLY_FILTER_TRIGGER } from '../../../../../src/plugins/ui_actions/public';
|
||||
|
@ -26,7 +26,6 @@ import {
|
|||
Query,
|
||||
RefreshInterval,
|
||||
} from '../../../../../src/plugins/data/public';
|
||||
import { GisMap } from '../connected_components/gis_map';
|
||||
import { createMapStore, MapStore } from '../reducers/store';
|
||||
import { MapSettings } from '../reducers/map';
|
||||
import {
|
||||
|
@ -43,7 +42,6 @@ import {
|
|||
setHiddenLayers,
|
||||
setMapSettings,
|
||||
} from '../actions/map_actions';
|
||||
import { MapCenterAndZoom } from '../../common/descriptor_types';
|
||||
import { setReadOnly, setIsLayerTOCOpen, setOpenTOCDetails } from '../actions/ui_actions';
|
||||
import { getIsLayerTOCOpen, getOpenTOCDetails } from '../selectors/ui_selectors';
|
||||
import {
|
||||
|
@ -56,36 +54,14 @@ import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants';
|
|||
import { RenderToolTipContent } from '../layers/tooltips/tooltip_property';
|
||||
import { getUiActions, getCoreI18n } from '../kibana_services';
|
||||
|
||||
interface MapEmbeddableConfig {
|
||||
editUrl?: string;
|
||||
indexPatterns: IIndexPattern[];
|
||||
editable: boolean;
|
||||
title?: string;
|
||||
layerList: unknown[];
|
||||
settings?: MapSettings;
|
||||
}
|
||||
|
||||
export interface MapEmbeddableInput extends EmbeddableInput {
|
||||
timeRange?: TimeRange;
|
||||
filters: Filter[];
|
||||
query?: Query;
|
||||
refreshConfig: RefreshInterval;
|
||||
isLayerTOCOpen: boolean;
|
||||
openTOCDetails?: string[];
|
||||
disableTooltipControl?: boolean;
|
||||
disableInteractive?: boolean;
|
||||
hideToolbarOverlay?: boolean;
|
||||
hideLayerControl?: boolean;
|
||||
hideViewControl?: boolean;
|
||||
mapCenter?: MapCenterAndZoom;
|
||||
hiddenLayers?: string[];
|
||||
hideFilterActions?: boolean;
|
||||
}
|
||||
import { MapEmbeddableInput, MapEmbeddableConfig } from './types';
|
||||
export { MapEmbeddableInput, MapEmbeddableConfig };
|
||||
|
||||
export interface MapEmbeddableOutput extends EmbeddableOutput {
|
||||
indexPatterns: IIndexPattern[];
|
||||
}
|
||||
|
||||
const GisMap = lazy(() => import('../connected_components/gis_map'));
|
||||
export class MapEmbeddable extends Embeddable<MapEmbeddableInput, MapEmbeddableOutput> {
|
||||
type = MAP_SAVED_OBJECT_TYPE;
|
||||
|
||||
|
@ -254,10 +230,12 @@ export class MapEmbeddable extends Embeddable<MapEmbeddableInput, MapEmbeddableO
|
|||
render(
|
||||
<Provider store={this._store}>
|
||||
<I18nContext>
|
||||
<GisMap
|
||||
addFilters={this.input.hideFilterActions ? null : this.addFilters}
|
||||
renderTooltipContent={this._renderTooltipContent}
|
||||
/>
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<GisMap
|
||||
addFilters={this.input.hideFilterActions ? null : this.addFilters}
|
||||
renderTooltipContent={this._renderTooltipContent}
|
||||
/>
|
||||
</Suspense>
|
||||
</I18nContext>
|
||||
</Provider>,
|
||||
this._domNode
|
||||
|
|
|
@ -6,24 +6,67 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { AnyAction } from 'redux';
|
||||
import { IIndexPattern } from 'src/plugins/data/public';
|
||||
// @ts-ignore
|
||||
import { getMapsSavedObjectLoader } from '../angular/services/gis_map_saved_object_loader';
|
||||
import { MapEmbeddable, MapEmbeddableInput } from './map_embeddable';
|
||||
import { getIndexPatternService, getHttp, getMapsCapabilities } from '../kibana_services';
|
||||
import {
|
||||
Embeddable,
|
||||
EmbeddableFactoryDefinition,
|
||||
IContainer,
|
||||
} from '../../../../../src/plugins/embeddable/public';
|
||||
|
||||
import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants';
|
||||
|
||||
import { createMapStore } from '../reducers/store';
|
||||
import { addLayerWithoutDataSync } from '../actions/map_actions';
|
||||
import { getQueryableUniqueIndexPatternIds } from '../selectors/map_selectors';
|
||||
import { getInitialLayers } from '../angular/get_initial_layers';
|
||||
import { mergeInputWithSavedMap } from './merge_input_with_saved_map';
|
||||
import '../index.scss';
|
||||
import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants';
|
||||
import { MapStore, MapStoreState } from '../reducers/store';
|
||||
import { MapEmbeddableConfig, MapEmbeddableInput } from './types';
|
||||
import { MapEmbeddableOutput } from './map_embeddable';
|
||||
import { RenderToolTipContent } from '../layers/tooltips/tooltip_property';
|
||||
import { EventHandlers } from '../reducers/non_serializable_instances';
|
||||
|
||||
let whenModulesLoadedPromise: Promise<boolean>;
|
||||
|
||||
let getMapsSavedObjectLoader: any;
|
||||
let MapEmbeddable: new (
|
||||
config: MapEmbeddableConfig,
|
||||
initialInput: MapEmbeddableInput,
|
||||
parent?: IContainer,
|
||||
renderTooltipContent?: RenderToolTipContent,
|
||||
eventHandlers?: EventHandlers
|
||||
) => Embeddable<MapEmbeddableInput, MapEmbeddableOutput>;
|
||||
|
||||
let getIndexPatternService: () => {
|
||||
get: (id: string) => IIndexPattern | undefined;
|
||||
};
|
||||
let getHttp: () => any;
|
||||
let getMapsCapabilities: () => any;
|
||||
let createMapStore: () => MapStore;
|
||||
let addLayerWithoutDataSync: (layerDescriptor: unknown) => AnyAction;
|
||||
let getQueryableUniqueIndexPatternIds: (state: MapStoreState) => string[];
|
||||
let getInitialLayers: (layerListJSON?: string, initialLayers?: unknown[]) => unknown[];
|
||||
let mergeInputWithSavedMap: any;
|
||||
|
||||
async function waitForMapDependencies(): Promise<boolean> {
|
||||
if (typeof whenModulesLoadedPromise !== 'undefined') {
|
||||
return whenModulesLoadedPromise;
|
||||
}
|
||||
|
||||
whenModulesLoadedPromise = new Promise(async resolve => {
|
||||
({
|
||||
// @ts-ignore
|
||||
getMapsSavedObjectLoader,
|
||||
getQueryableUniqueIndexPatternIds,
|
||||
MapEmbeddable,
|
||||
getIndexPatternService,
|
||||
getHttp,
|
||||
getMapsCapabilities,
|
||||
createMapStore,
|
||||
addLayerWithoutDataSync,
|
||||
getInitialLayers,
|
||||
mergeInputWithSavedMap,
|
||||
} = await import('./lazy'));
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
return whenModulesLoadedPromise;
|
||||
}
|
||||
|
||||
export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
|
||||
type = MAP_SAVED_OBJECT_TYPE;
|
||||
|
@ -36,6 +79,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
};
|
||||
|
||||
async isEditable() {
|
||||
await waitForMapDependencies();
|
||||
return getMapsCapabilities().save as boolean;
|
||||
}
|
||||
|
||||
|
@ -53,7 +97,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
async _getIndexPatterns(layerList: unknown[]): Promise<IIndexPattern[]> {
|
||||
// Need to extract layerList from store to get queryable index pattern ids
|
||||
const store = createMapStore();
|
||||
let queryableIndexPatternIds;
|
||||
let queryableIndexPatternIds: string[];
|
||||
try {
|
||||
layerList.forEach((layerDescriptor: unknown) => {
|
||||
store.dispatch(addLayerWithoutDataSync(layerDescriptor));
|
||||
|
@ -69,6 +113,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
|
||||
const promises = queryableIndexPatternIds.map(async indexPatternId => {
|
||||
try {
|
||||
// @ts-ignore
|
||||
return await getIndexPatternService().get(indexPatternId);
|
||||
} catch (error) {
|
||||
// Unable to load index pattern, better to not throw error so map embeddable can render
|
||||
|
@ -90,6 +135,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
input: MapEmbeddableInput,
|
||||
parent?: IContainer
|
||||
) => {
|
||||
await waitForMapDependencies();
|
||||
const savedMap = await this._fetchSavedMap(savedObjectId);
|
||||
const layerList = getInitialLayers(savedMap.layerListJSON);
|
||||
const indexPatterns = await this._getIndexPatterns(layerList);
|
||||
|
@ -129,6 +175,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
};
|
||||
|
||||
create = async (input: MapEmbeddableInput, parent?: IContainer) => {
|
||||
await waitForMapDependencies();
|
||||
const layerList = getInitialLayers();
|
||||
const indexPatterns = await this._getIndexPatterns(layerList);
|
||||
|
||||
|
|
38
x-pack/plugins/maps/public/embeddable/types.ts
Normal file
38
x-pack/plugins/maps/public/embeddable/types.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { IIndexPattern } from '../../../../../src/plugins/data/common/index_patterns';
|
||||
import { MapSettings } from '../reducers/map';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { EmbeddableInput } from '../../../../../src/plugins/embeddable/public/lib/embeddables';
|
||||
import { Filter, Query, RefreshInterval, TimeRange } from '../../../../../src/plugins/data/common';
|
||||
import { MapCenterAndZoom } from '../../common/descriptor_types';
|
||||
|
||||
export interface MapEmbeddableConfig {
|
||||
editUrl?: string;
|
||||
indexPatterns: IIndexPattern[];
|
||||
editable: boolean;
|
||||
title?: string;
|
||||
layerList: unknown[];
|
||||
settings?: MapSettings;
|
||||
}
|
||||
|
||||
export interface MapEmbeddableInput extends EmbeddableInput {
|
||||
timeRange?: TimeRange;
|
||||
filters: Filter[];
|
||||
query?: Query;
|
||||
refreshConfig: RefreshInterval;
|
||||
isLayerTOCOpen: boolean;
|
||||
openTOCDetails?: string[];
|
||||
disableTooltipControl?: boolean;
|
||||
disableInteractive?: boolean;
|
||||
hideToolbarOverlay?: boolean;
|
||||
hideLayerControl?: boolean;
|
||||
hideViewControl?: boolean;
|
||||
mapCenter?: MapCenterAndZoom;
|
||||
hiddenLayers?: string[];
|
||||
hideFilterActions?: boolean;
|
||||
}
|
|
@ -36,11 +36,10 @@ import {
|
|||
import { featureCatalogueEntry } from './feature_catalogue_entry';
|
||||
// @ts-ignore
|
||||
import { getMapsVisTypeAlias } from './maps_vis_type_alias';
|
||||
import { registerLayerWizards } from './layers/load_layer_wizards';
|
||||
import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
|
||||
import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public';
|
||||
import { MAP_SAVED_OBJECT_TYPE } from '../common/constants';
|
||||
import { MapEmbeddableFactory } from './embeddable';
|
||||
import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory';
|
||||
import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public';
|
||||
|
||||
export interface MapsPluginSetupDependencies {
|
||||
|
@ -85,7 +84,6 @@ export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => {
|
|||
setUiActions(plugins.uiActions);
|
||||
setNavigation(plugins.navigation);
|
||||
setCoreI18n(core.i18n);
|
||||
registerLayerWizards();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue