- Создана структура docs/ с категориями: completed, guides, architecture, diagnostic, in-progress, reports - Перемещено 47+ MD файлов из корня проекта в docs/ - Удалено 6 устаревших/дублирующихся файлов - Создана структура backend/scripts/ для тестов и утилит - Перемещено 35 Python скриптов в backend/scripts/ - Создана структура backend/docs/ для технической документации - Перемещено 12 MD файлов backend в backend/docs/ - Перемещены LDAP тестовые данные в backend/tests/fixtures/ - Добавлены README файлы для навигации - Добавлены правила в .github/copilot-instructions.md для размещения документации Результат: - Корень проекта: только README.md - Корень backend/: только manage.py и Django приложения - Вся документация организована в структурированные папки
8.4 KiB
8.4 KiB
Устранение FOUC (Flash of Unstyled Content)
Проблема
При переходе между страницами происходило мигание белой темы, даже если пользователь выбрал тёмную тему.
Ошибочное предположение
❌ "Стили не кэшируются" - НЕверно!
- Статические файлы (CSS/JS) всегда кэшируются браузером
- Nginx выдаёт их с
Cache-Control: public, max-age=31536000 - Проблема была НЕ в кэше!
Настоящая причина
✅ Неправильный порядок выполнения скриптов:
1. HTML загружается с data-bs-theme="auto"
2. CSS загружается и применяется (тема = auto = светлая)
3. 🔥 МИГАНИЕ БЕЛОЙ ТЕМЫ 🔥
4. JavaScript выполняется и меняет тему на тёмную
Решение
До (неправильно)
<html data-bs-theme="auto">
<head>
<link rel="stylesheet" href="bootstrap.css">
<!-- Стили применяются с auto = светлая тема -->
</head>
<body>
<script defer>
// Слишком поздно! Уже видели белую тему
document.documentElement.setAttribute('data-bs-theme', 'dark');
</script>
</body>
</html>
После (правильно)
<html>
<head>
<!-- 1. СРАЗУ применяем тему ДО загрузки CSS -->
<script src="themeInitializer.js"></script>
<!-- 2. Preload критичных стилей -->
<link rel="preload" href="bootstrap.css" as="style">
<link rel="preload" href="variables.css" as="style">
<!-- 3. Теперь загружаем CSS - тема уже установлена! -->
<link rel="stylesheet" href="bootstrap.css">
<link rel="stylesheet" href="variables.css">
</head>
<body>
<!-- Никакого мигания! -->
</body>
</html>
Как работает themeInitializer.js
Синхронное выполнение (НЕ defer, НЕ async, НЕ module)
(function() {
'use strict';
// 1. Читаем сохранённую тему из localStorage
const savedTheme = localStorage.getItem('theme') || 'auto';
// 2. Определяем эффективную тему
let effectiveTheme = savedTheme;
if (savedTheme === 'auto') {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
effectiveTheme = prefersDark.matches ? 'dark' : 'light';
}
// 3. НЕМЕДЛЕННО устанавливаем атрибут
document.documentElement.setAttribute('data-bs-theme', effectiveTheme);
// Всё это происходит ДО парсинга <body> и ДО загрузки CSS!
})();
Почему это работает
- ✅ Скрипт выполняется синхронно в
<head> - ✅ Браузер блокируется до выполнения скрипта
- ✅ Атрибут
data-bs-themeустанавливается ДО применения CSS - ✅ CSS применяется сразу с правильной темой
- ✅ Никакого мигания!
Preload критичных стилей
Зачем нужен preload?
<link rel="preload" href="bootstrap.css" as="style">
<link rel="stylesheet" href="bootstrap.css">
Без preload:
- Браузер парсит HTML
- Доходит до
<link rel="stylesheet"> - Начинает загрузку CSS (задержка!)
- Парсит CSS
- Применяет стили
С preload:
- Браузер парсит HTML
- Видит
<link rel="preload">→ начинает загрузку параллельно - Доходит до
<link rel="stylesheet">→ CSS уже загружен! - Сразу применяет стили
Результат: ускорение на 100-300ms
Кэширование и FOUC
Важно понимать!
Кэш НЕ решает проблему FOUC:
С кэшем (неправильный порядок):
1. HTML из disk cache (0ms)
2. CSS из disk cache (0ms) ← быстро!
3. 🔥 Но всё равно мигание! 🔥
4. JS меняет тему (слишком поздно)
С кэшем (правильный порядок):
1. HTML из disk cache (0ms)
2. themeInitializer.js СИНХРОННО (0ms)
3. data-bs-theme установлен ДО парсинга CSS
4. CSS из disk cache (0ms) применяется с правильной темой
5. ✅ Никакого мигания!
Вывод:
- Кэш ускоряет загрузку
- Правильный порядок устраняет FOUC
- Нужны оба решения!
Тестирование
Как проверить, что FOUC устранён
- Выберите тёмную тему на сайте
- Hard refresh (Ctrl+Shift+R) для сброса кэша
- Быстро переходите между страницами
- Результат: НЕ должно быть белых вспышек
Проверка в DevTools
// Откройте Console
// Проверьте, что атрибут установлен ДО DOMContentLoaded
console.log('Theme on parse:', document.documentElement.getAttribute('data-bs-theme'));
// Должно вывести 'dark' или 'light', НЕ 'auto'
Проверка порядка загрузки
Network tab → фильтр "CSS":
- ✅
themeInitializer.js- загружен первым - ✅
bootstrap.css(preload) - ✅
variables.css(preload) - ✅ Остальные стили
Частые ошибки
❌ Использование defer/async
<!-- НЕПРАВИЛЬНО: defer выполнится ПОСЛЕ парсинга -->
<script src="themeInitializer.js" defer></script>
<!-- ПРАВИЛЬНО: синхронное выполнение -->
<script src="themeInitializer.js"></script>
❌ Использование type="module"
<!-- НЕПРАВИЛЬНО: модули выполняются с defer -->
<script type="module" src="themeInitializer.js"></script>
<!-- ПРАВИЛЬНО: обычный скрипт -->
<script src="themeInitializer.js"></script>
❌ Установка темы в DOMContentLoaded
// НЕПРАВИЛЬНО: слишком поздно!
document.addEventListener('DOMContentLoaded', () => {
document.documentElement.setAttribute('data-bs-theme', 'dark');
});
// ПРАВИЛЬНО: немедленно
document.documentElement.setAttribute('data-bs-theme', 'dark');
❌ data-bs-theme="auto" в HTML
<!-- НЕПРАВИЛЬНО: браузер применит auto ДО JS -->
<html data-bs-theme="auto">
<!-- ПРАВИЛЬНО: без атрибута, JS установит сразу -->
<html>
<head>
<script>/* устанавливаем тему */</script>
Совместимость с кэшированием
Статика кэшируется правильно
themeInitializer.js:
- Cache-Control: public, max-age=31536000
- Loaded: (disk cache)
bootstrap.css:
- Cache-Control: public, max-age=31536000
- Loaded: (disk cache)
HTML кэшируется с правильным порядком
base.html:
- Cache-Control: private, max-age=60
- Loaded: (disk cache)
- Порядок скриптов сохранён!
Результат:
- ✅ Мгновенная загрузка из кэша
- ✅ Без мигания темы
- ✅ Идеальный UX!
Резюме
| Проблема | Решение | Результат |
|---|---|---|
| Мигание белой темы | Синхронный скрипт в <head> |
✅ Нет FOUC |
| Медленная загрузка CSS | Preload критичных стилей | ✅ +100-300ms |
| "Стили не кэшируются" | Это было заблуждение | ✅ Всегда кэшировались |
| data-bs-theme="auto" | Убрали из HTML | ✅ JS устанавливает сразу |
Главный вывод: Проблема была НЕ в кэшировании, а в порядке выполнения! 🎯