slicejs-web-framework 1.0.24 → 1.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -3,6 +3,14 @@ export default class Router {
|
|
|
3
3
|
this.routes = routes;
|
|
4
4
|
this.activeRoute = null;
|
|
5
5
|
this.pathToRouteMap = this.createPathToRouteMap(routes);
|
|
6
|
+
|
|
7
|
+
// NUEVO: Sistema de caché optimizado
|
|
8
|
+
this.routeContainersCache = new Map();
|
|
9
|
+
this.lastCacheUpdate = 0;
|
|
10
|
+
this.CACHE_DURATION = 100; // ms - caché muy corto pero efectivo
|
|
11
|
+
|
|
12
|
+
// NUEVO: Observer para invalidar caché automáticamente
|
|
13
|
+
this.setupMutationObserver();
|
|
6
14
|
}
|
|
7
15
|
|
|
8
16
|
async init() {
|
|
@@ -10,13 +18,55 @@ export default class Router {
|
|
|
10
18
|
window.addEventListener('popstate', this.onRouteChange.bind(this));
|
|
11
19
|
}
|
|
12
20
|
|
|
21
|
+
// NUEVO: Observer para detectar cambios en el DOM
|
|
22
|
+
setupMutationObserver() {
|
|
23
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
24
|
+
this.observer = new MutationObserver((mutations) => {
|
|
25
|
+
let shouldInvalidateCache = false;
|
|
26
|
+
|
|
27
|
+
mutations.forEach((mutation) => {
|
|
28
|
+
if (mutation.type === 'childList') {
|
|
29
|
+
// Solo invalidar si se añadieron/removieron nodos que podrían ser rutas
|
|
30
|
+
const addedNodes = Array.from(mutation.addedNodes);
|
|
31
|
+
const removedNodes = Array.from(mutation.removedNodes);
|
|
32
|
+
|
|
33
|
+
const hasRouteNodes = [...addedNodes, ...removedNodes].some(node =>
|
|
34
|
+
node.nodeType === Node.ELEMENT_NODE &&
|
|
35
|
+
(node.tagName === 'SLICE-ROUTE' ||
|
|
36
|
+
node.tagName === 'SLICE-MULTI-ROUTE' ||
|
|
37
|
+
node.querySelector?.('slice-route, slice-multi-route'))
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (hasRouteNodes) {
|
|
41
|
+
shouldInvalidateCache = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (shouldInvalidateCache) {
|
|
47
|
+
this.invalidateCache();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
this.observer.observe(document.body, {
|
|
52
|
+
childList: true,
|
|
53
|
+
subtree: true
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// NUEVO: Invalidar caché
|
|
59
|
+
invalidateCache() {
|
|
60
|
+
this.routeContainersCache.clear();
|
|
61
|
+
this.lastCacheUpdate = 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
13
64
|
createPathToRouteMap(routes, basePath = '', parentRoute = null) {
|
|
14
65
|
const pathToRouteMap = new Map();
|
|
15
66
|
|
|
16
67
|
for (const route of routes) {
|
|
17
68
|
const fullPath = `${basePath}${route.path}`.replace(/\/+/g, '/');
|
|
18
69
|
|
|
19
|
-
// Guardar la referencia a la ruta padre
|
|
20
70
|
const routeWithParent = {
|
|
21
71
|
...route,
|
|
22
72
|
fullPath,
|
|
@@ -25,13 +75,12 @@ export default class Router {
|
|
|
25
75
|
};
|
|
26
76
|
|
|
27
77
|
pathToRouteMap.set(fullPath, routeWithParent);
|
|
28
|
-
|
|
29
|
-
// Si tiene rutas secundarias, también agregarlas al mapa
|
|
78
|
+
|
|
30
79
|
if (route.children) {
|
|
31
80
|
const childPathToRouteMap = this.createPathToRouteMap(
|
|
32
81
|
route.children,
|
|
33
82
|
fullPath,
|
|
34
|
-
routeWithParent
|
|
83
|
+
routeWithParent
|
|
35
84
|
);
|
|
36
85
|
|
|
37
86
|
for (const [childPath, childRoute] of childPathToRouteMap.entries()) {
|
|
@@ -43,32 +92,109 @@ export default class Router {
|
|
|
43
92
|
return pathToRouteMap;
|
|
44
93
|
}
|
|
45
94
|
|
|
46
|
-
|
|
47
|
-
|
|
95
|
+
// OPTIMIZADO: Sistema de caché inteligente
|
|
96
|
+
async renderRoutesComponentsInPage(searchContainer = document) {
|
|
48
97
|
let routerContainersFlag = false;
|
|
98
|
+
const routeContainers = this.getCachedRouteContainers(searchContainer);
|
|
49
99
|
|
|
50
100
|
for (const routeContainer of routeContainers) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
101
|
+
try {
|
|
102
|
+
// Verificar que el componente aún esté conectado al DOM
|
|
103
|
+
if (!routeContainer.isConnected) {
|
|
104
|
+
this.invalidateCache();
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let response = await routeContainer.renderIfCurrentRoute();
|
|
109
|
+
if (response) {
|
|
110
|
+
this.activeRoute = routeContainer.props;
|
|
111
|
+
routerContainersFlag = true;
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
slice.logger.logError('Router', `Error rendering route container`, error);
|
|
55
115
|
}
|
|
56
116
|
}
|
|
117
|
+
|
|
57
118
|
return routerContainersFlag;
|
|
58
119
|
}
|
|
59
120
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
121
|
+
// NUEVO: Obtener contenedores con caché
|
|
122
|
+
getCachedRouteContainers(container) {
|
|
123
|
+
const containerKey = container === document ? 'document' : container.sliceId || 'anonymous';
|
|
124
|
+
const now = Date.now();
|
|
125
|
+
|
|
126
|
+
// Verificar si el caché es válido
|
|
127
|
+
if (this.routeContainersCache.has(containerKey) &&
|
|
128
|
+
(now - this.lastCacheUpdate) < this.CACHE_DURATION) {
|
|
129
|
+
return this.routeContainersCache.get(containerKey);
|
|
130
|
+
}
|
|
63
131
|
|
|
64
|
-
|
|
65
|
-
|
|
132
|
+
// Regenerar caché
|
|
133
|
+
const routeContainers = this.findAllRouteContainersOptimized(container);
|
|
134
|
+
this.routeContainersCache.set(containerKey, routeContainers);
|
|
135
|
+
this.lastCacheUpdate = now;
|
|
136
|
+
|
|
137
|
+
return routeContainers;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// OPTIMIZADO: Búsqueda más eficiente usando TreeWalker
|
|
141
|
+
findAllRouteContainersOptimized(container) {
|
|
142
|
+
const routeContainers = [];
|
|
143
|
+
|
|
144
|
+
// Usar TreeWalker para una búsqueda más eficiente
|
|
145
|
+
const walker = document.createTreeWalker(
|
|
146
|
+
container,
|
|
147
|
+
NodeFilter.SHOW_ELEMENT,
|
|
148
|
+
{
|
|
149
|
+
acceptNode: (node) => {
|
|
150
|
+
// Solo aceptar nodos que sean slice-route o slice-multi-route
|
|
151
|
+
if (node.tagName === 'SLICE-ROUTE' || node.tagName === 'SLICE-MULTI-ROUTE') {
|
|
152
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
153
|
+
}
|
|
154
|
+
return NodeFilter.FILTER_SKIP;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
let node;
|
|
160
|
+
while (node = walker.nextNode()) {
|
|
161
|
+
routeContainers.push(node);
|
|
66
162
|
}
|
|
67
163
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
164
|
+
return routeContainers;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// NUEVO: Método específico para renderizar rutas dentro de un componente
|
|
168
|
+
async renderRoutesInComponent(component) {
|
|
169
|
+
if (!component) {
|
|
170
|
+
slice.logger.logWarning('Router', 'No component provided for route rendering');
|
|
171
|
+
return false;
|
|
71
172
|
}
|
|
173
|
+
|
|
174
|
+
return await this.renderRoutesComponentsInPage(component);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// OPTIMIZADO: Debouncing para evitar múltiples llamadas seguidas
|
|
178
|
+
async onRouteChange() {
|
|
179
|
+
// Cancelar el timeout anterior si existe
|
|
180
|
+
if (this.routeChangeTimeout) {
|
|
181
|
+
clearTimeout(this.routeChangeTimeout);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Debounce de 10ms para evitar múltiples llamadas seguidas
|
|
185
|
+
this.routeChangeTimeout = setTimeout(async () => {
|
|
186
|
+
const path = window.location.pathname;
|
|
187
|
+
const routeContainersFlag = await this.renderRoutesComponentsInPage();
|
|
188
|
+
|
|
189
|
+
if (routeContainersFlag) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const { route, params } = this.matchRoute(path);
|
|
194
|
+
if (route) {
|
|
195
|
+
await this.handleRoute(route, params);
|
|
196
|
+
}
|
|
197
|
+
}, 10);
|
|
72
198
|
}
|
|
73
199
|
|
|
74
200
|
async navigate(path) {
|
|
@@ -79,7 +205,6 @@ export default class Router {
|
|
|
79
205
|
async handleRoute(route, params) {
|
|
80
206
|
const targetElement = document.querySelector('#app');
|
|
81
207
|
|
|
82
|
-
// Si tenemos una ruta con parentRoute, usamos el componente del padre
|
|
83
208
|
const componentName = route.parentRoute ? route.parentRoute.component : route.component;
|
|
84
209
|
const sliceId = `route-${componentName}`;
|
|
85
210
|
|
|
@@ -105,9 +230,10 @@ export default class Router {
|
|
|
105
230
|
targetElement.appendChild(component);
|
|
106
231
|
}
|
|
107
232
|
|
|
233
|
+
// Invalidar caché después de cambios importantes en el DOM
|
|
234
|
+
this.invalidateCache();
|
|
108
235
|
await this.renderRoutesComponentsInPage();
|
|
109
236
|
|
|
110
|
-
|
|
111
237
|
if (slice.loading) {
|
|
112
238
|
slice.loading.stop();
|
|
113
239
|
}
|
|
@@ -124,22 +250,18 @@ export default class Router {
|
|
|
124
250
|
}
|
|
125
251
|
|
|
126
252
|
matchRoute(path) {
|
|
127
|
-
// 1. Buscar coincidencia exacta en el mapa
|
|
128
253
|
const exactMatch = this.pathToRouteMap.get(path);
|
|
129
254
|
if (exactMatch) {
|
|
130
|
-
// Si es una ruta hija y tiene un padre definido, devolvemos el padre en su lugar
|
|
131
255
|
if (exactMatch.parentRoute) {
|
|
132
|
-
// Mantenemos la información de parámetros que viene de la ruta actual
|
|
133
256
|
return {
|
|
134
257
|
route: exactMatch.parentRoute,
|
|
135
258
|
params: {},
|
|
136
|
-
childRoute: exactMatch
|
|
259
|
+
childRoute: exactMatch
|
|
137
260
|
};
|
|
138
261
|
}
|
|
139
262
|
return { route: exactMatch, params: {} };
|
|
140
263
|
}
|
|
141
264
|
|
|
142
|
-
// 2. Buscar coincidencias en rutas con parámetros
|
|
143
265
|
for (const [routePattern, route] of this.pathToRouteMap.entries()) {
|
|
144
266
|
if (routePattern.includes('${')) {
|
|
145
267
|
const { regex, paramNames } = this.compilePathPattern(routePattern);
|
|
@@ -150,7 +272,6 @@ export default class Router {
|
|
|
150
272
|
params[name] = match[i + 1];
|
|
151
273
|
});
|
|
152
274
|
|
|
153
|
-
// Si es una ruta hija, devolvemos el padre con los parámetros
|
|
154
275
|
if (route.parentRoute) {
|
|
155
276
|
return {
|
|
156
277
|
route: route.parentRoute,
|
|
@@ -164,7 +285,6 @@ export default class Router {
|
|
|
164
285
|
}
|
|
165
286
|
}
|
|
166
287
|
|
|
167
|
-
// 3. Si no hay coincidencias, retornar la ruta 404
|
|
168
288
|
const notFoundRoute = this.pathToRouteMap.get('/404');
|
|
169
289
|
return { route: notFoundRoute, params: {} };
|
|
170
290
|
}
|