mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-11-10 11:55:56 +00:00
perf: fix slow page loads and gate performance in CI
- Ship one hashed Tailwind stylesheet instead of two (one render-blocking) - Compress responses in-app (brotli/gzip, streaming-safe) and precompress the node build so local and CI measurements match production - Validate the Supabase session once per request - Render the homepage immediately and stream public stats - Stream a Pokedex's entries with the page instead of fetching after hydration, running the rows and count queries in parallel - Shrink the avatar and offline placeholder images, fix layout shift, contrast, link names and missing meta descriptions - Add Lighthouse CI (mobile + desktop) with score and metric budgets, bundle-size budgets in the build tests, and signed-in speed scenarios
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
import { error, redirect } from '@sveltejs/kit';
|
||||
import PokedexRepository from '$lib/repositories/PokedexRepository';
|
||||
import { loadCombinedDataPage } from '$lib/services/CombinedDataService';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
// Must match the page's itemsPerPage: the box view needs the whole dex in one page.
|
||||
const INITIAL_PAGE_SIZE = 9999;
|
||||
|
||||
export const load: PageServerLoad = async ({ locals, params }) => {
|
||||
const { safeGetSession, supabase } = locals;
|
||||
const { session, user } = await safeGetSession();
|
||||
@@ -22,7 +26,22 @@ export const load: PageServerLoad = async ({ locals, params }) => {
|
||||
throw error(404, 'Pokédex not found');
|
||||
}
|
||||
|
||||
// Streamed rather than awaited: the page shell renders straight away and the entries arrive in
|
||||
// the same response, instead of the browser requesting them after hydration. A failure resolves
|
||||
// to null so the page falls back to fetching (and reporting) through the API.
|
||||
const initialCombinedData = loadCombinedDataPage(supabase, user.id, pokedex, {
|
||||
page: 1,
|
||||
limit: INITIAL_PAGE_SIZE,
|
||||
enableForms: pokedex.isFormDex
|
||||
})
|
||||
.then((result) => result.combinedData)
|
||||
.catch((err) => {
|
||||
console.error('Unable to preload combined data', err);
|
||||
return null;
|
||||
});
|
||||
|
||||
return {
|
||||
pokedex
|
||||
pokedex,
|
||||
initialCombinedData
|
||||
};
|
||||
};
|
||||
|
||||
@@ -502,8 +502,35 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch data whenever pagination controls change (client-side only)
|
||||
$: if (browser && pokedexId) getData({ page: currentPage, perPage: itemsPerPage });
|
||||
// Show data whenever the dex or pagination changes (client-side only). The first page is streamed
|
||||
// from the server load, so it only needs fetching when that failed or the page changes.
|
||||
let shownKey = '';
|
||||
function showPage(
|
||||
id: string,
|
||||
page: number,
|
||||
perPage: number,
|
||||
initial: Promise<CombinedData[] | null> | undefined
|
||||
) {
|
||||
const key = `${id}:${page}:${perPage}`;
|
||||
if (key === shownKey) return;
|
||||
shownKey = key;
|
||||
if (page !== 1 || !initial) {
|
||||
void getData({ page, perPage });
|
||||
return;
|
||||
}
|
||||
combinedData = null;
|
||||
void initial.then((rows) => {
|
||||
if (shownKey !== key) return;
|
||||
if (!rows) {
|
||||
void getData({ page, perPage });
|
||||
return;
|
||||
}
|
||||
combinedData = rows;
|
||||
boxNumbers = calculateBoxNumbers(rows.length);
|
||||
});
|
||||
}
|
||||
$: if (browser && pokedexId)
|
||||
showPage(pokedexId, currentPage, itemsPerPage, data?.initialCombinedData);
|
||||
|
||||
onMount(() => {
|
||||
if (!browser) return;
|
||||
|
||||
Reference in New Issue
Block a user