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:
chamikaJ
2025-07-07 12:41:23 +05:30
parent 26b47aac53
commit aa1fb1c6f5
7 changed files with 583 additions and 177 deletions

View File

@@ -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;