Perf: pre-compile JSX, prod React, WebP assets, immutable cache
This is the big "make scrolling smooth" pass — the biggest wins live in cold-start, not in the scroll loop itself, but a fast cold start means GSAP starts in a non-thrashing state and stays there. - Drop @babel/standalone (~3MB) from the page entirely. app.jsx is pre-compiled to app.js via deploy/build.sh; the browser loads the compiled bundle directly. JSX-in-browser was running babel transform on every page load, which is brutal on phones. - Switch React UMD bundles from .development to .production.min: ~1.1MB → ~140KB, no dev-mode warnings/checks in the hot path. - Add `defer` + `<link rel=preload as=script>` for the React/GSAP CDN scripts so they fetch in parallel with HTML parse, execute in order after DOM is ready, and don't block first paint. - Re-encode the three 1.4–1.8MB PNG backgrounds as WebP at full size (~190KB total) plus a 900px-wide mobile variant (~52KB total). Mobile preload links use `media=` so phones never download the full-size variants. - Move bg-image URLs from inline JSX styles into styles.css so the mobile media query can swap them in cleanly. - nginx: long-cache versioned static assets (Cache-Control immutable, 1 year) since URLs already carry ?v=… cache busters; keep the HTML itself on must-revalidate so the version pointer can update. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
27
deploy/build.sh
Executable file
27
deploy/build.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
# Re-compile app.jsx -> app.js so the browser can load it directly,
|
||||
# without shipping ~3MB of @babel/standalone to every visitor.
|
||||
#
|
||||
# Usage: deploy/build.sh
|
||||
# Run this before `git commit` whenever app.jsx changes.
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
ROOT="$PWD"
|
||||
|
||||
# Use a dedicated build dir so we don't litter the repo root with
|
||||
# node_modules / package-lock.json.
|
||||
BUILD_DIR=".build"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
if [ ! -f "$BUILD_DIR/package.json" ]; then
|
||||
( cd "$BUILD_DIR" && npm init -y >/dev/null \
|
||||
&& npm install --silent --no-audit --no-fund \
|
||||
@babel/core@7 @babel/cli@7 @babel/preset-react@7 )
|
||||
fi
|
||||
|
||||
"$BUILD_DIR/node_modules/.bin/babel" app.jsx \
|
||||
--presets=@babel/preset-react \
|
||||
-o app.js
|
||||
|
||||
echo "Built app.js ($(wc -c < app.js) bytes)"
|
||||
@@ -17,28 +17,38 @@ server {
|
||||
try_files /facere.html =404;
|
||||
}
|
||||
|
||||
# The HTML entrypoint must NOT cache long — it carries the
|
||||
# ?v=… cache-busting tags that everything else relies on.
|
||||
location = /facere.html {
|
||||
add_header Cache-Control "public, max-age=0, must-revalidate";
|
||||
expires off;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /facere.html;
|
||||
}
|
||||
|
||||
# CSS/JS/JSX: short cache so future deploys are picked up promptly
|
||||
# CSS/JS: long-cache + immutable. URLs are versioned with ?v=… so
|
||||
# each deploy is a brand-new key; anything served here will never
|
||||
# need to be re-fetched in a session.
|
||||
location ~* \.(?:css|js|jsx)$ {
|
||||
expires 5m;
|
||||
add_header Cache-Control "public, max-age=300, must-revalidate";
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
types { text/css css; application/javascript js; application/javascript jsx; }
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Static media: long cache (filenames are stable / change when content changes)
|
||||
location ~* \.(?:png|jpe?g|gif|webp|svg|mp4|webm|woff2?|ttf|otf|ico)$ {
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, max-age=604800";
|
||||
# Static media: long cache + immutable (filenames change when content does).
|
||||
location ~* \.(?:png|jpe?g|gif|webp|avif|svg|mp4|webm|woff2?|ttf|otf|ico)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
|
||||
gzip_min_length 1024;
|
||||
gzip_vary on;
|
||||
|
||||
access_log /var/log/nginx/facere.access.log;
|
||||
error_log /var/log/nginx/facere.error.log;
|
||||
|
||||
Reference in New Issue
Block a user