بنقرة واحدة
wsh-home-optimization
// Home page performance optimization — Post defaultScope, SSR data reduction, lazy media hydration for WSH 2026 CaX app
// Home page performance optimization — Post defaultScope, SSR data reduction, lazy media hydration for WSH 2026 CaX app
Crok AI chat rendering optimization — SSE debouncing, ChatMessage memoization, Markdown re-render prevention
DM page and flow optimization — afterSave hook, message rendering, unread count queries for WSH 2026
Lazy loading modal containers and route components to reduce initial bundle size — CrokContainer, NewPostModalContainer
SSE streaming optimization — batching char-by-char events and debouncing React re-renders for Crok AI chat performance
Runs Visual Regression Testing (VRT) locally to prevent disqualification in Web Speed Hackathon. Captures screenshots, compares against baselines, updates snapshots, and validates visual integrity after performance optimizations. Use when optimizing WSH apps, running VRT checks, updating VRT baselines, or investigating VRT failures.
Optimizes deliberately slow web applications for maximum Lighthouse scores in Web Speed Hackathon (CyberAgent). Use when participating in WSH or performing aggressive frontend performance optimization on React/Node.js apps with SQLite backends. Covers bundle reduction, image optimization, Core Web Vitals, server tuning, and known competition traps.
| name | wsh-home-optimization |
| description | Home page performance optimization — Post defaultScope, SSR data reduction, lazy media hydration for WSH 2026 CaX app |
The home page (ホームを開く) scores 29.35/100 — the worst scoring page. Root causes are heavy Post defaultScope, massive SSR JSON, and expensive hydration of 10 timeline items with all media components.
Use this skill when optimizing the home page timeline, Post model queries, or SSR prefetch for list views.
The Post model's defaultScope eagerly loads user, images, movie, sound associations for every query. For the home timeline, this creates heavy JOINs and inflates SSR JSON.
// Current: server/src/models/Post.ts defaultScope
defaultScope: {
include: [
{ association: "user", include: [{ association: "profileImage" }] },
{ association: "images", through: { attributes: [] } },
{ association: "movie" },
{ association: "sound" },
],
}
// Approach: Create a lightweight scope for list views
// Option A: Use unscoped() + explicit includes in list endpoints
// Option B: Create named scopes (e.g., 'list' vs 'detail')
Key constraint: Single post detail pages still need all associations. Only list views (home timeline, user posts, search results) can use the lighter scope.
Impact: FCP, LCP, SI improvement by reducing SSR response time and JSON size.
SoundPlayer and PausableMovie are heavy components that block hydration:
// Lazy load approach for timeline items
const SoundArea = React.lazy(() => import('./SoundArea'));
const MovieArea = React.lazy(() => import('./MovieArea'));
// In TimelineItem:
{post.sound && (
<Suspense fallback={<div style={{ height: 80 }} />}>
<SoundArea sound={post.sound} />
</Suspense>
)}
Impact: TBT improvement by deferring heavy component initialization.
The SSR prefetch serializes full Post objects with all associations as inline JSON:
window.__SSR_DATA__ contains 10 posts × (user + images + movie + sound)<script> blocks FCP (HTML parsing takes longer)Options:
Impact: FCP, LCP by reducing HTML size.
| Pitfall | Symptom | Fix |
|---|---|---|
| Removing associations breaks detail pages | Single post pages show no images/video/audio | Only apply light scope to list endpoints, keep full scope for detail |
| Lazy loading breaks SSR hydration | Hydration mismatch errors in console | Use Suspense with appropriate fallback that matches server HTML |
| Changing defaultScope affects other queries | Various pages break | Use Post.unscoped().findAll() with explicit includes instead of modifying defaultScope |
| Change | Visual Impact | Mitigation |
|---|---|---|
| Removing associations from list view | Media might not render on timeline | Ensure images still load (they're needed for timeline display) |
| Lazy loading media | Slight layout shift during load | Use fixed-height placeholders matching current dimensions |
| Reducing SSR data | Initial flash of loading states | Ensure above-fold content still SSR renders correctly |
server/src/models/Post.tsserver/src/ssr-prefetch.tsserver/src/routes/api/post.tsclient/src/components/timeline/client/src/components/foundation/SoundPlayer.tsxclient/src/components/foundation/PausableMovie.tsxPost.findAll({ limit: 10, offset: 0 })