Skip to content

Commit 1c9c7c5

Browse files
author
danhnguyen
committed
📦refactor(layers, map, sources): optimize layer and source handling with markRaw
1 parent bdbd998 commit 1c9c7c5

5 files changed

Lines changed: 154 additions & 44 deletions

File tree

‎libs/composables/layers/useCreateLayer.ts‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import { shallowRef, unref, computed, watch, ref } from 'vue';
2-
import type { CreateBaseLayerActions, Nullable, LayerTypes } from '@libs/types';
1+
import { useLogger, useMapReloadEvent } from '@libs/composables';
32
import { getNanoid, hasLayer, hasSource } from '@libs/helpers';
4-
import { useMapReloadEvent, useLogger } from '@libs/composables';
5-
import type { MaybeRef } from 'vue';
3+
import type { CreateBaseLayerActions, LayerTypes, Nullable } from '@libs/types';
64
import type {
7-
SourceSpecification,
85
FilterSpecification,
6+
LayerSpecification,
97
Map,
8+
SourceSpecification,
109
StyleSetterOptions,
11-
LayerSpecification,
1210
} from 'maplibre-gl';
11+
import type { MaybeRef } from 'vue';
12+
import { computed, markRaw, ref, shallowRef, unref, watch } from 'vue';
1313

1414
/**
1515
* Layer creation status enum for better state management
@@ -297,7 +297,8 @@ export function useCreateLayer<Layer extends LayerSpecification>(
297297
} as LayerSpecification;
298298

299299
map.addLayer(layerSpec, beforeId);
300-
layer.value = map.getLayer(layerId) as unknown as Layer;
300+
// Use markRaw to prevent Vue reactivity overhead on MapLibre layer objects
301+
layer.value = markRaw(map.getLayer(layerId) as unknown as Layer);
301302
layerStatus.value = LayerStatus.Created;
302303

303304
// Register the enhanced actions
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
export * from './useCreateMaplibre';
2-
export * from './useMaplibre';
3-
export * from './useLayer';
41
export * from './useCreateImage';
5-
export * from './useGeoJsonSource';
6-
export * from './useCreatePopup';
2+
export * from './useCreateMaplibre';
73
export * from './useCreateMarker';
4+
export * from './useCreatePopup';
5+
export * from './useGeoJsonSource';
6+
export * from './useLayer';
7+
export * from './useMaplibre';
8+
export * from './useMaplibreConfig';

‎libs/composables/map/useCreateMaplibre.ts‎

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
1-
import {
2-
ref,
3-
shallowRef,
4-
computed,
5-
watchEffect,
6-
unref,
7-
onUnmounted,
8-
} from 'vue';
91
import { useLogger } from '@libs/composables';
102
import { MapCreationStatus } from '@libs/enums';
113
import { type CreateMaplibreActions } from '@libs/types';
12-
import type { MaybeRef } from 'vue';
13-
import { Map } from 'maplibre-gl';
144
import type {
15-
MapOptions,
5+
CameraOptions,
166
LngLatBoundsLike,
177
LngLatLike,
8+
MapOptions,
9+
StyleOptions,
1810
StyleSpecification,
19-
CameraOptions,
2011
StyleSwapOptions,
21-
StyleOptions,
2212
} from 'maplibre-gl';
13+
import { Map } from 'maplibre-gl';
14+
import type { MaybeRef } from 'vue';
15+
import {
16+
computed,
17+
markRaw,
18+
onUnmounted,
19+
ref,
20+
shallowRef,
21+
unref,
22+
watchEffect,
23+
} from 'vue';
2324

2425
interface CreateMaplibreProps extends MapOptions {
2526
register?: (actions: SimplifiedCreateMaplibreActions) => void;
@@ -153,11 +154,15 @@ export function useCreateMaplibre(
153154
try {
154155
const mapOpts = unref(mapOptions);
155156

156-
mapInstance.value = new Map({
157-
...mapOpts,
158-
style,
159-
container: el,
160-
});
157+
// Use markRaw to prevent Vue's reactivity system from deeply observing
158+
// MapLibre's internal objects - this is a critical performance optimization
159+
mapInstance.value = markRaw(
160+
new Map({
161+
...mapOpts,
162+
style,
163+
container: el,
164+
}),
165+
);
161166

162167
// Use explicit type assertion to avoid deep type instantiation
163168
currentStyle.value = style as any;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { useLogger } from '@libs/composables';
2+
import {
3+
clearPrewarmedResources,
4+
prewarm,
5+
setMaxParallelImageRequests,
6+
setWorkerCount,
7+
} from 'maplibre-gl';
8+
import { onUnmounted } from 'vue';
9+
10+
/**
11+
* Configuration options for MapLibre GL performance optimization
12+
*/
13+
export interface MaplibreConfigOptions {
14+
/** Number of web workers for tile loading (default: 4) */
15+
workerCount?: number;
16+
/** Maximum parallel image requests (default: 16) */
17+
maxParallelImageRequests?: number;
18+
/** Whether to prewarm MapLibre resources on initialization (default: true) */
19+
prewarmResources?: boolean;
20+
/** Enable debug logging */
21+
debug?: boolean;
22+
}
23+
24+
/**
25+
* Actions returned by useMaplibreConfig
26+
*/
27+
export interface MaplibreConfigActions {
28+
/** Clears prewarmed resources - call on app unmount to release memory */
29+
clearPrewarmedResources: () => void;
30+
}
31+
32+
/**
33+
* Composable for configuring MapLibre GL global performance settings.
34+
*
35+
* This should be called once at the application level (e.g., in App.vue or main.ts)
36+
* to optimize MapLibre performance across all map instances.
37+
*
38+
* @example
39+
* ```vue
40+
* <script setup>
41+
* import { useMaplibreConfig } from 'vue3-maplibre-gl';
42+
*
43+
* // Initialize MapLibre with optimized settings
44+
* useMaplibreConfig({
45+
* workerCount: 4,
46+
* maxParallelImageRequests: 16,
47+
* prewarmResources: true,
48+
* });
49+
* </script>
50+
* ```
51+
*
52+
* @param options - Configuration options for MapLibre performance
53+
* @returns Actions for managing MapLibre configuration
54+
*/
55+
export function useMaplibreConfig(
56+
options: MaplibreConfigOptions = {},
57+
): MaplibreConfigActions {
58+
const {
59+
workerCount = 4,
60+
maxParallelImageRequests = 16,
61+
prewarmResources = true,
62+
debug = false,
63+
} = options;
64+
65+
const { log, logError } = useLogger(debug);
66+
67+
try {
68+
// Configure web worker count for parallel tile loading
69+
setWorkerCount(workerCount);
70+
log('MapLibre worker count set to:', workerCount);
71+
72+
// Configure maximum parallel image requests
73+
setMaxParallelImageRequests(maxParallelImageRequests);
74+
log(
75+
'MapLibre max parallel image requests set to:',
76+
maxParallelImageRequests,
77+
);
78+
79+
// Prewarm MapLibre resources for faster initial map render
80+
if (prewarmResources) {
81+
prewarm();
82+
log('MapLibre resources prewarmed');
83+
}
84+
} catch (error) {
85+
logError('Error configuring MapLibre:', error);
86+
}
87+
88+
// Cleanup prewarmed resources on unmount
89+
onUnmounted(() => {
90+
try {
91+
clearPrewarmedResources();
92+
log('MapLibre prewarmed resources cleared');
93+
} catch (error) {
94+
logError('Error clearing prewarmed resources:', error);
95+
}
96+
});
97+
98+
return {
99+
clearPrewarmedResources,
100+
};
101+
}

‎libs/composables/sources/useCreateGeoJsonSource.ts‎

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
1-
import {
2-
unref,
3-
shallowRef,
4-
computed,
5-
onUnmounted,
6-
onMounted,
7-
nextTick,
8-
ref,
9-
} from 'vue';
1+
import { useLogger, useMapReloadEvent } from '@libs/composables';
102
import { getMainVersion, getNanoid, hasSource } from '@libs/helpers';
11-
import { useMapReloadEvent, useLogger } from '@libs/composables';
12-
import type { MaybeRef, ShallowRef } from 'vue';
133
import type { Nullable } from '@libs/types';
144
import type {
15-
Map,
165
GeoJSONSource,
17-
MapSourceDataEvent,
186
GeoJSONSourceSpecification,
7+
Map,
8+
MapSourceDataEvent,
199
} from 'maplibre-gl';
10+
import type { MaybeRef, ShallowRef } from 'vue';
11+
import {
12+
computed,
13+
markRaw,
14+
nextTick,
15+
onMounted,
16+
onUnmounted,
17+
ref,
18+
shallowRef,
19+
unref,
20+
} from 'vue';
2021

2122
/**
2223
* Source creation status enum for better state management
@@ -108,7 +109,8 @@ export function useCreateGeoJsonSource({
108109
if (getMainVersion() > 0) isSourceLoaded = true;
109110

110111
if (!source.value && e.sourceId === sourceId && isSourceLoaded) {
111-
source.value = map.getSource(sourceId) as GeoJSONSource;
112+
// Use markRaw to prevent Vue reactivity overhead on MapLibre source objects
113+
source.value = markRaw(map.getSource(sourceId) as GeoJSONSource);
112114
sourceStatus.value = SourceStatus.Created;
113115

114116
register?.(

0 commit comments

Comments
 (0)