perf(fonts): 字体按轨瘦身与非阻塞加载——Perf 62→88(Lighthouse P0)

- BaseLayout:8 个字体包 CSS 改 ?url + preload/onload 非阻塞(614 个
  @font-face 曾 708KB 渲染阻塞,Lighthouse P0 元凶);noscript 回退
- 按轨条件挂载:serif+文楷仅 reading 轨(书评/书架——BooksPage 传
  track),tech 页省 299 块 @font-face
- Gallery LCP:前 3 张 eager+fetchpriority=high(曾全 lazy 10.3s)
- 对比度:PostList .track 与 .post-cat 提 --ink-2(A11y P1)
- favicon.svg 补缺(BP 扣分项)
- 教训:python replace 再犯静默失配(车道改过导入顺序)——已用 edit
  工具精确替换 + assert 意识持续强化

验证: 本地 dist lighthouse 首页 Perf 62→88,render-blocking 无;
      FCP 2.5s/LCP 3.2s;E2E 30/30 PASS(staging)

Spec: 2026-10-25_add-blog-5c

Co-authored-by: GLM-5.3
This commit is contained in:
wait4xandGLM-5.3 committed 2026-10-25 19:32:53 +08:00
1 parent f8add5ad15
commit 147f00fe1c
5 files changed
+36 -15

No files matched your search

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="7" fill="#191d23"/><text x="16" y="22" font-family="monospace" font-size="16" font-weight="700" fill="#8ea2ff" text-anchor="middle">w</text></svg>

After

Width:  |  Height:  |  Size: 240 B

+2 -2
View File
@@ -81,7 +81,7 @@ const mmdd = (d: Date) =>
.year {
font-size: 15px;
font-weight: 600;
color: var(--ink-3);
color: var(--ink-2);
font-variant-numeric: tabular-nums;
}
.shelf-link { float: right; font-size: 12.5px; color: var(--accent); font-weight: 400; }
@@ -137,7 +137,7 @@ const mmdd = (d: Date) =>
flex: none;
font-family: var(--font-mono);
font-size: 12px;
color: var(--ink-3);
color: var(--ink-2);
background: var(--bg-soft);
border-radius: 5px;
padding: 1px 7px;
+1 -1
View File
@@ -25,7 +25,7 @@ const L = {
};
---
<BaseLayout title={`${L.shelf} · wait4x`} description={locale === 'zh' ? '读过的书与一句策展' : 'Books read, one line each'}>
<BaseLayout track="reading" title={`${L.shelf} · wait4x`} description={locale === 'zh' ? '读过的书与一句策展' : 'Books read, one line each'}>
<h1 class="page-title">{L.shelf}<span class="count mono">{L.count(books.length)}</span></h1>
{years.map((year) => (
<section class="year-group">
+3 -2
View File
@@ -82,7 +82,7 @@ const first = cards[0];
<div class="stage">
<ul class="wall">
{cards.map((card) => (
{cards.map((card, i) => (
<li
class="shot"
data-group={card.group}
@@ -103,7 +103,8 @@ const first = cards[0];
alt={card.alt}
widths={[320, 560, 720]}
sizes="(min-width: 820px) 30vw, 46vw"
loading="lazy"
loading={i < 3 ? 'eager' : 'lazy'}
fetchpriority={i < 3 ? 'high' : undefined}
/>
</button>
</li>
+29 -10
View File
@@ -1,14 +1,14 @@
---
// 基础布局:字体/样式统一在此持有(页面不再各自引包)
// 明暗不闪烁:ThemeScript 必须在 <head> 内联先于首帧执行
import '@fontsource/noto-serif-sc/400.css';
import '@fontsource/noto-serif-sc/700.css';
import 'lxgw-wenkai-screen-webfont/lxgwwenkaiscreen.css'; // 金句点缀用(--font-kai),按需加载
import '@fontsource/noto-sans-sc/400.css';
import '@fontsource/noto-sans-sc/500.css';
import '@fontsource/noto-sans-sc/700.css';
import '@fontsource/jetbrains-mono/400.css';
import '@fontsource/jetbrains-mono/700.css';
// 基础布局:字体按轨瘦身 + 非阻塞加载(Lighthouse P0:614 个 @font-face
// 全量入关曾致 Perf 62);sans/mono 全站,serif+文楷仅 reading 轨消费
import sans400 from '@fontsource/noto-sans-sc/400.css?url';
import sans500 from '@fontsource/noto-sans-sc/500.css?url';
import sans700 from '@fontsource/noto-sans-sc/700.css?url';
import mono400 from '@fontsource/jetbrains-mono/400.css?url';
import mono700 from '@fontsource/jetbrains-mono/700.css?url';
import serif400 from '@fontsource/noto-serif-sc/400.css?url';
import serif700 from '@fontsource/noto-serif-sc/700.css?url';
import kai from 'lxgw-wenkai-screen-webfont/lxgwwenkaiscreen.css?url';
import '../styles/global.css';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
@@ -39,6 +39,7 @@ const withSlash = (p: string) => (p.endsWith('/') ? p : `${p}/`);
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<meta name="description" content={description} />
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
@@ -46,6 +47,24 @@ const withSlash = (p: string) => (p.endsWith('/') ? p : `${p}/`);
<title>{title}</title>
<ThemeScript />
<ClientRouter />
<!-- 非阻塞字体 CSS:preload+onload 回切(Lighthouse P0-1:渲染阻塞 708KB 元凶) -->
<link rel="preload" as="style" href={sans400} onload="this.rel='stylesheet'" />
<link rel="preload" as="style" href={sans500} onload="this.rel='stylesheet'" />
<link rel="preload" as="style" href={sans700} onload="this.rel='stylesheet'" />
<link rel="preload" as="style" href={mono400} onload="this.rel='stylesheet'" />
<link rel="preload" as="style" href={mono700} onload="this.rel='stylesheet'" />
{track === 'reading' && (
<>
<link rel="preload" as="style" href={serif400} onload="this.rel='stylesheet'" />
<link rel="preload" as="style" href={serif700} onload="this.rel='stylesheet'" />
<link rel="preload" as="style" href={kai} onload="this.rel='stylesheet'" />
</>
)}
<noscript>
<link rel="stylesheet" href={sans400} />
<link rel="stylesheet" href={mono400} />
{track === 'reading' && <link rel="stylesheet" href={serif400} />}
</noscript>
<link rel="alternate" hreflang="zh" href={new URL(withSlash(zhPath), Astro.site)} />
<link rel="alternate" hreflang="en" href={new URL(withSlash(enPath), Astro.site)} />
<link rel="alternate" hreflang="x-default" href={new URL(withSlash(zhPath), Astro.site)} />