feat(performance): optimize resource loading and initialization
- Added resource hints in index.html for improved loading performance, including preconnect and dns-prefetch links. - Implemented preload for critical JSON resources to enhance initial load times. - Optimized Google Analytics and HubSpot script loading to defer execution and reduce blocking during initial render. - Refactored app initialization in App.tsx to defer non-critical operations, improving perceived performance. - Introduced lazy loading for chart components and TinyMCE editor to minimize initial bundle size and enhance user experience. - Enhanced Vite configuration for optimized chunking strategy and improved caching with shorter hash lengths.
This commit is contained in:
@@ -6,16 +6,27 @@ import logger from './utils/errorLogger';
|
||||
// Essential namespaces that should be preloaded to prevent Suspense
|
||||
const ESSENTIAL_NAMESPACES = [
|
||||
'common',
|
||||
'auth/login',
|
||||
'navbar',
|
||||
];
|
||||
|
||||
// Secondary namespaces that can be loaded on demand
|
||||
const SECONDARY_NAMESPACES = [
|
||||
'tasks/task-table-bulk-actions',
|
||||
'task-management',
|
||||
'auth/login',
|
||||
'settings',
|
||||
'home',
|
||||
'project-drawer',
|
||||
];
|
||||
|
||||
// Cache to track loaded translations and prevent duplicate requests
|
||||
const loadedTranslations = new Set<string>();
|
||||
const loadingPromises = new Map<string, Promise<any>>();
|
||||
|
||||
// Background loading queue for non-essential translations
|
||||
let backgroundLoadingQueue: Array<{ lang: string; ns: string }> = [];
|
||||
let isBackgroundLoading = false;
|
||||
|
||||
i18n
|
||||
.use(HttpApi)
|
||||
.use(initReactI18next)
|
||||
@@ -23,24 +34,34 @@ i18n
|
||||
fallbackLng: 'en',
|
||||
backend: {
|
||||
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
||||
// Add request timeout to prevent hanging on slow connections
|
||||
requestOptions: {
|
||||
cache: 'default',
|
||||
mode: 'cors',
|
||||
credentials: 'same-origin',
|
||||
},
|
||||
},
|
||||
defaultNS: 'common',
|
||||
// Only load essential namespaces initially
|
||||
ns: ESSENTIAL_NAMESPACES,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
// Preload essential namespaces
|
||||
preload: ['en', 'es', 'pt', 'alb', 'de'],
|
||||
// Load all namespaces on initialization
|
||||
// Only preload current language to reduce initial load
|
||||
preload: [],
|
||||
load: 'languageOnly',
|
||||
// Cache translations
|
||||
// Disable loading all namespaces on init
|
||||
initImmediate: false,
|
||||
// Cache translations with shorter expiration for better performance
|
||||
cache: {
|
||||
enabled: true,
|
||||
expirationTime: 24 * 60 * 60 * 1000, // 24 hours
|
||||
expirationTime: 12 * 60 * 60 * 1000, // 12 hours
|
||||
},
|
||||
// Reduce debug output in production
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
});
|
||||
|
||||
// Utility function to ensure translations are loaded
|
||||
// Optimized function to ensure translations are loaded
|
||||
export const ensureTranslationsLoaded = async (
|
||||
namespaces: string[] = ESSENTIAL_NAMESPACES,
|
||||
languages: string[] = [i18n.language || 'en']
|
||||
@@ -65,7 +86,6 @@ export const ensureTranslationsLoaded = async (
|
||||
|
||||
// Create loading promise
|
||||
const loadingPromise = new Promise<void>((resolve, reject) => {
|
||||
// Switch to the target language temporarily if needed
|
||||
const currentLang = i18n.language;
|
||||
const shouldSwitchLang = currentLang !== lang;
|
||||
|
||||
@@ -77,7 +97,6 @@ export const ensureTranslationsLoaded = async (
|
||||
|
||||
await i18n.loadNamespaces(ns);
|
||||
|
||||
// Switch back to original language if we changed it
|
||||
if (shouldSwitchLang && currentLang) {
|
||||
await i18n.changeLanguage(currentLang);
|
||||
}
|
||||
@@ -100,7 +119,6 @@ export const ensureTranslationsLoaded = async (
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all loading promises to complete
|
||||
await Promise.all(loadPromises);
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -109,13 +127,86 @@ export const ensureTranslationsLoaded = async (
|
||||
}
|
||||
};
|
||||
|
||||
// Preload essential translations for current language only on startup
|
||||
const initializeTranslations = async () => {
|
||||
const currentLang = i18n.language || 'en';
|
||||
await ensureTranslationsLoaded(ESSENTIAL_NAMESPACES, [currentLang]);
|
||||
// Background loading function for non-essential translations
|
||||
const processBackgroundQueue = async () => {
|
||||
if (isBackgroundLoading || backgroundLoadingQueue.length === 0) return;
|
||||
|
||||
isBackgroundLoading = true;
|
||||
|
||||
try {
|
||||
// Process queue in batches to avoid overwhelming the network
|
||||
const batchSize = 3;
|
||||
while (backgroundLoadingQueue.length > 0) {
|
||||
const batch = backgroundLoadingQueue.splice(0, batchSize);
|
||||
const batchPromises = batch.map(({ lang, ns }) =>
|
||||
ensureTranslationsLoaded([ns], [lang]).catch(error => {
|
||||
logger.error(`Background loading failed for ${lang}:${ns}`, error);
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(batchPromises);
|
||||
|
||||
// Add small delay between batches to prevent blocking
|
||||
if (backgroundLoadingQueue.length > 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
isBackgroundLoading = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize translations on app startup (only once)
|
||||
// Queue secondary translations for background loading
|
||||
const queueSecondaryTranslations = (language: string) => {
|
||||
SECONDARY_NAMESPACES.forEach(ns => {
|
||||
const key = `${language}:${ns}`;
|
||||
if (!loadedTranslations.has(key)) {
|
||||
backgroundLoadingQueue.push({ lang: language, ns });
|
||||
}
|
||||
});
|
||||
|
||||
// Start background loading with a delay to not interfere with initial render
|
||||
setTimeout(processBackgroundQueue, 2000);
|
||||
};
|
||||
|
||||
// Initialize only essential translations for current language
|
||||
const initializeTranslations = async () => {
|
||||
try {
|
||||
const currentLang = i18n.language || 'en';
|
||||
|
||||
// Load only essential namespaces initially
|
||||
await ensureTranslationsLoaded(ESSENTIAL_NAMESPACES, [currentLang]);
|
||||
|
||||
// Queue secondary translations for background loading
|
||||
queueSecondaryTranslations(currentLang);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error('Failed to initialize translations:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Language change handler that prioritizes essential namespaces
|
||||
export const changeLanguageOptimized = async (language: string) => {
|
||||
try {
|
||||
// Change language first
|
||||
await i18n.changeLanguage(language);
|
||||
|
||||
// Load essential namespaces immediately
|
||||
await ensureTranslationsLoaded(ESSENTIAL_NAMESPACES, [language]);
|
||||
|
||||
// Queue secondary translations for background loading
|
||||
queueSecondaryTranslations(language);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error(`Failed to change language to ${language}:`, error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize translations on app startup (only essential ones)
|
||||
initializeTranslations();
|
||||
|
||||
export default i18n;
|
||||
|
||||
Reference in New Issue
Block a user