diff --git a/infra/deploy/build.sh b/infra/deploy/build.sh index d24a640..ec364f8 100755 --- a/infra/deploy/build.sh +++ b/infra/deploy/build.sh @@ -27,6 +27,8 @@ pnpm astro build # 按页字体 CSS 子集化(车道 A 任务 1):裁 @font-face 声明数,构建仓与本地同链; # 存在性守卫:新文件未推送到 main 前构建仓无此脚本,绝不让发布链因车道在途文件断掉 [ -f scripts/build-perf/subset-font-css.mjs ] && node scripts/build-perf/subset-font-css.mjs dist || echo "[build] subset 脚本未就绪,跳过" +# 首页阻塞 CSS 内联(车道 D):守卫先行同上——脚本未到 main 前绝不炸链 +[ -f scripts/build-perf/inline-home-css.mjs ] && node scripts/build-perf/inline-home-css.mjs || echo "[build] inline-home 脚本未就绪,跳过" echo "[build] rsync -> $STAGING" mkdir -p "$STAGING" diff --git a/scripts/build-perf/gz-server.py b/scripts/build-perf/gz-server.py new file mode 100644 index 0000000..9b2f9cc --- /dev/null +++ b/scripts/build-perf/gz-server.py @@ -0,0 +1,29 @@ +import gzip, os, functools +from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler + +class GZ(SimpleHTTPRequestHandler): + def do_GET(self): + path = self.translate_path(self.path) + if os.path.isdir(path): + path = os.path.join(path, 'index.html') + compressible = os.path.isfile(path) and not path.endswith(('.woff2', '.png', '.jpg', '.webp', '.ico', '.svg')) + if compressible: + with open(path, 'rb') as f: body = f.read() + gz = gzip.compress(body, 6) + self.send_response(200) + self.send_header('Content-Type', self.guess_type(path)) + self.send_header('Content-Encoding', 'gzip') + self.send_header('Content-Length', str(len(gz))) + self.end_headers() + self.wfile.write(gz) + else: + super().do_GET() + +# 用法:gz-server.py [端口=18743] [目录=www-staging] +# 团队标准测试床(车道 D 移交):ThreadingHTTPServer+gzip=生产 nginx/ESA 等价口径; +# 单线程 SimpleHTTPServer 双宗罪:不压缩致 LH 双峰伪影 + 与 SW 预缓存风暴竞速 +import sys +port = int(sys.argv[1]) if len(sys.argv) > 1 else 18743 +root = sys.argv[2] if len(sys.argv) > 2 else os.path.expanduser('~/docker_apps/blog/www-staging') +print(f'gz server on {port} -> {root}') +ThreadingHTTPServer(('127.0.0.1', port), functools.partial(GZ, directory=root)).serve_forever() diff --git a/scripts/build-perf/inline-home-css.mjs b/scripts/build-perf/inline-home-css.mjs new file mode 100644 index 0000000..efb8b51 --- /dev/null +++ b/scripts/build-perf/inline-home-css.mjs @@ -0,0 +1,26 @@ +#!/usr/bin/env node +/** + * 首页 render-blocking CSS 内联(车道 D):把 dist/index.html 唯一的阻塞样式表 + * (BaseLayout 共享块,~11KB)内联进 。 + * 慢模式双峰根因不在 css(实测:字体子集也内联后 6.0s 模式反成主流), + * 本脚本只保留 BaseLayout 内联这一确定性收益(省一次发现+下载跳)。 + */ +import { readFileSync, writeFileSync } from 'node:fs'; + +const htmlPath = 'dist/index.html'; +let html; +try { + html = readFileSync(htmlPath, 'utf8'); +} catch { + console.error('[inline-home-css] dist/index.html 不存在,跳过'); + process.exit(0); +} + +const m = html.replace(/