perf(state/assets): debounce saveState, defer SheetJS script (#11)
- saveState is now debounced (~400ms) to coalesce rapid changes; a pending write is flushed on beforeunload and on visibilitychange→hidden so the last change is never lost. - The SheetJS <script> gets defer so it no longer blocks initial parse; the typeof XLSX guard already covers any not-yet-available case. Verified headless: app loads without JS/CSP errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
8cd8908f38
commit
0efb9cb853
2 files changed
+18
-3
No files matched your search
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<script src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js" integrity="sha384-EnyY0/GSHQGSxSgMwaIPzSESbqoOLSexfnSMN2AP+39Ckmn92stwABZynq1JyzdT" crossorigin="anonymous"></script>
|
<script defer src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js" integrity="sha384-EnyY0/GSHQGSxSgMwaIPzSESbqoOLSexfnSMN2AP+39Ckmn92stwABZynq1JyzdT" crossorigin="anonymous"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
|||||||
@@ -30,9 +30,11 @@ try {
|
|||||||
// Corrupt storage — start fresh
|
// Corrupt storage — start fresh
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call after any state mutation to persist automatically
|
// Persist immediately. Used by the debounced saveState and the flush handlers.
|
||||||
let storageWarned = false;
|
let storageWarned = false;
|
||||||
export function saveState() {
|
let saveTimer = null;
|
||||||
|
function writeState() {
|
||||||
|
if (saveTimer !== null) { clearTimeout(saveTimer); saveTimer = null; }
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
||||||
employees: state.employees,
|
employees: state.employees,
|
||||||
@@ -52,6 +54,19 @@ export function saveState() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call after any state mutation. Debounced to coalesce rapid changes; a pending
|
||||||
|
// write is flushed on page hide/unload so the last change is never lost.
|
||||||
|
export function saveState() {
|
||||||
|
if (saveTimer !== null) clearTimeout(saveTimer);
|
||||||
|
saveTimer = setTimeout(writeState, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const flushPending = () => { if (saveTimer !== null) writeState(); };
|
||||||
|
window.addEventListener('beforeunload', flushPending);
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (document.visibilityState === 'hidden') flushPending();
|
||||||
|
});
|
||||||
|
|
||||||
// Tab switching
|
// Tab switching
|
||||||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
|
|||||||
Reference in new issue
Block a user