Files
blog/themes/Ying/OPTIMIZATION_STEP1_PLAN.md
T
Vaica cbf2034f78 perf: JS按需加载优化 + PJAX适配
- 首页JS减少56%(800KB → 350KB)
- 文章页JS减少44%(800KB → 450KB)
- 完美适配PJAX导航
- 保持所有功能正常
2026-06-03 12:29:56 +08:00

706 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第1步:CSS加载策略优化实施方案
## 📋 当前状态分析
### CSS文件清单
- `normalize.css` - CSS重置(基础,必须首屏加载)
- `remixicon.css` - 图标字体(必须首屏加载)
- `uikit.min.css` - UIkit框架(必须首屏加载)
- `nprogress.min.css` - 进度条(核心功能,首屏加载)
- `message.min.css` - 消息提示(可延迟)
- `main.css` - 主题样式(需要拆分)
### main.css 结构分析
#### 首屏关键CSS(约15-20KB)
```css
1. CSS变量定义(:root)
2. @font-face 字体声明
3. html/body 基础样式
4. .main 容器布局(居中、阴影、背景)
5. .header 头部布局
6. .header-menu 导航菜单
7. .header-avatar 头像
8. .header-social 社交链接
9. .header-bio 个人介绍
10. [data-theme="dark"] 深色模式基础变量
11. 基础排版(h1-h6, p, a, ul)
12. .footer 页脚
13. 滚动条样式
14. 基础图片样式
15. hr 分割线
```
#### 非首屏CSS(可延迟加载,约35-40KB)
```css
1. 代码相关
- .code-fold-container
- .code-expand-btn
- pre, code 特殊样式
- 代码高亮主题
2. 文章详情页
- blockquote 样式
- 文章列表特殊样式
- 打赏按钮(.reward-container)
- 相关文章(.related-posts)
3. 评论系统
- Artalk 相关样式(.atk-*)
- 评论区布局
4. 友链页面
- .links-item
- .link-active-badge
- .link-dead-badge
5. 弹幕系统
- .barrage-container
- 弹幕动画
6. 搜索功能
- .search-modal
- .search-results
7. 其他功能
- 浮动工具(.floating-tools)
- Toast 提示
- 图片灯箱
- 无限滚动加载提示
```
---
## 🎯 优化方案
### 策略1:CSS代码拆分(推荐)
将 `main.css` 拆分为两个文件:
- `critical.css` - 首屏关键CSS(内联到HTML)
- `main.css` - 其余样式(异步加载)
### 策略2:CSS层级加载
保留单个 `main.css`,但使用Hugo的模板逻辑分层输出:
- 首屏样式:内联 `<style>` 标签
- 其余样式:`<link rel="preload">` 异步加载
---
## 📝 实施步骤
### 步骤1.1:创建关键CSS文件
**文件路径:** `themes/Ying/assets/css/critical.css`
**内容:** 从main.css提取首屏关键样式
```css
/* ========================================
首屏关键CSS - Critical CSS
用于内联到<head>中,提升FCP
======================================== */
/* CSS变量 */
:root {
--background-color: #fafafa;
--background-dark-color: #020617;
--text-content-color: #24292e;
--text-content-dark-color: #d1d5db;
--text-title-color: #111827;
--text-title-dark-color: #e5e7eb;
--text-highlight-color: #696565;
--frame-dark-color: #1f2933;
--frame-color: #eee;
--card-shadow-light: 0 10px 20px 0 rgba(236, 236, 236, 0.86);
--card-shadow-dark: 0 18px 45px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(148, 163, 184, 0.45);
--link-color-light: #374151;
--link-hover-color-light: #111827;
--link-color-dark: #e5e7eb;
--link-hover-color-dark: #d1d5db;
}
/* 字体声明 */
@font-face {
font-family: 'zql';
src: url('../font/zql-v2.woff2') format('woff2'),
url('../font/zql-v2.woff') format('woff');
font-display: swap;
unicode-range: U+0000-007F, U+4E00-9FFF, U+2000-206F, U+3000-303F;
}
/* 基础样式 */
html {
scroll-behavior: smooth;
}
body {
font-family: 'zql', serif;
color: var(--text-content-color);
line-height: 1.6;
font-size: 16px;
background: var(--background-color);
/* 背景图片 */
background-image: url(data:image/webp;base64,...); /* 实际base64数据
background-repeat: round;
background-size: contain;
background-attachment: fixed;
z-index: 5;
transition: background-color 0.3s ease, color 0.3s ease;
}
/* 深色模式基础 */
[data-theme="dark"] body {
color: var(--text-content-dark-color);
background-color: var(--background-dark-color);
}
[data-theme="dark"] .main {
background: rgba(15, 23, 42, 0.99);
box-shadow: var(--card-shadow-dark);
}
/* 主容器 */
.main {
max-width: 618px;
overflow: hidden;
margin: 60px auto;
padding: 50px;
background: rgba(255, 255, 255, 0.8);
box-shadow: var(--card-shadow-light);
border-radius: 8px;
z-index: 10;
position: relative;
text-align: center;
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
transition: background-color 0.5s cubic-bezier(0.4, 0, 0.2, 1),
color 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
/* 头部布局 */
.header {
margin-bottom: 10px;
padding-bottom: 10px;
display: flex;
flex-direction: column;
align-items: stretch;
}
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.header-avatar {
width: 80px;
height: 80px;
border-radius: 50%;
overflow: hidden;
cursor: pointer;
position: relative;
}
.header-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
box-shadow: none;
}
.header-info {
text-align: right;
}
.header-social {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-bottom: 8px;
}
.header-social a {
color: var(--link-color-light);
font-size: 1.2rem;
transition: color 0.3s;
}
.header-social a:hover {
color: var(--link-hover-color-light);
}
.header-bio {
font-size: 0.9rem;
color: #666;
}
/* 导航菜单 */
.header-menu {
border-top: 1px solid #eee;
padding-top: 10px;
}
.header-menu ul {
display: flex;
justify-content: center;
list-style: none;
gap: 25px;
padding: 0;
margin: 0;
}
.header-menu ul li a {
color: var(--link-color-light);
text-decoration: none;
font-size: 0.95rem;
transition: color 0.3s;
}
.header-menu ul li.active a {
color: var(--text-title-color);
font-weight: 600;
}
/* 深色模式导航 */
[data-theme="dark"] .header nav {
border-top-color: #1f2933;
}
[data-theme="dark"] .header nav ul.flat li a {
color: #9ca3af;
}
[data-theme="dark"] .header nav ul.flat li.active a {
color: #e5e7eb;
}
[data-theme="dark"] .header-social a {
color: var(--link-color-dark);
}
[data-theme="dark"] .header-social a:hover {
color: var(--link-hover-color-dark);
}
[data-theme="dark"] .header-bio {
color: #9ca3af;
}
/* 排版 */
h1, h2, h3, h4, h5, h6 {
line-height: 1.2;
font-weight: 400;
}
h1 { font-size: 1.5rem; }
h2 { font-size: 1.375rem; }
h3 { font-size: 1.25rem; }
h4, h5, h6 { font-size: 1.125rem; }
a {
color: var(--link-color-light);
text-decoration: none;
transition: color 0.3s;
}
a:hover {
color: var(--link-hover-color-light);
}
[data-theme="dark"] a {
color: var(--link-color-dark);
}
[data-theme="dark"] a:hover {
color: var(--link-hover-color-dark);
}
p {
margin: 0.8em 0;
}
ul {
padding-left: 0px;
list-style-type: none;
}
/* 图片基础样式 */
img {
max-width: 100%;
border-radius: 5px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
vertical-align: middle;
}
a img {
border: none;
}
/* 分割线 */
hr {
display: block;
height: 1px;
border: 0;
border-top: 1px solid #cccccc;
margin: 1rem 0;
padding: 0;
}
[data-theme="dark"] hr {
border-top-color: rgba(255, 255, 255, 0.2);
}
/* 滚动条 */
::-webkit-scrollbar {
width: 13px;
}
::-webkit-scrollbar-button {
display: none;
}
::-webkit-scrollbar-thumb {
min-height: 16px;
background-color: #999999;
background-clip: padding-box;
border: 3px solid #fdfdfd;
border-radius: 5px;
}
::selection {
background: #444;
color: #fff;
}
/* 图标 */
.icon {
width: 1.8em;
height: 1.8em;
vertical-align: -1em;
fill: currentColor;
overflow: hidden;
}
/* 文章元信息 */
.meta {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 10px;
margin-bottom: 20px;
color: #999;
font-size: 0.9rem;
line-height: 1.6;
}
.meta-item {
white-space: nowrap;
}
[data-theme="dark"] .meta {
color: #9ca3af;
}
/* 页脚 */
.footer {
border-top: 1px solid #eee;
padding-top: 20px;
margin-top: 30px;
color: #999;
font-size: 0.85rem;
}
[data-theme="dark"] .footer {
border-top-color: #1f2933;
color: #6b7280;
}
/* 文章标题 */
.post-title {
color: var(--text-title-color);
margin-bottom: 0.5em;
}
[data-theme="dark"] .post-title {
color: var(--text-title-dark-color);
}
/* 深色模式链接和文本 */
[data-theme="dark"] .post-content {
color: var(--text-content-dark-color);
}
/* 主题切换按钮 */
.theme-toggle-button {
border: none;
background: transparent;
cursor: pointer;
font-size: 14px;
padding: 4px 8px;
color: #586069;
}
.theme-toggle-button:focus {
outline: none;
}
[data-theme="dark"] .theme-toggle-button {
color: #e5e7eb;
}
/* 响应式 - 大屏优化 */
@media (min-width: 768px) {
.main {
background: rgba(255, 255, 255, 0.88);
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
}
}
/* 主题切换动画 */
#theme-toggle i {
transition: transform 0.5s cubic-bezier(0.4, 0.0, 0.2, 1);
}
#theme-toggle.rotate i {
transform: rotate(360deg);
}
```
---
### 步骤1.2:修改 head.html - 实施CSS加载策略
**文件路径:** `themes/Ying/layouts/partials/head.html`
**修改位置:** 第127-135行(Stylesheets部分)
**当前代码:**
```gohtml
{{ $normalize := resources.Get "css/libs/normalize.css" }}
{{ $remixicon := resources.Get "css/libs/remixicon.css" }}
{{ $uikit := resources.Get "css/libs/uikit.min.css" }}
{{ $nprogress := resources.Get "css/libs/nprogress.min.css" }}
{{ $message := resources.Get "css/libs/message.min.css" }}
{{ $main := resources.Get "css/main.css" }}
{{ $style := slice $normalize $remixicon $uikit $nprogress $message $main | resources.Concat "css/style.css" | resources.Minify | resources.Fingerprint }}
<link rel="stylesheet" href="{{ $style.RelPermalink }}">
```
**修改后代码:**
```gohtml
{{/* ====== CSS加载优化:关键CSS内联 + 非关键CSS异步加载 ====== */}}
{{/* 1. 首屏关键CSS - 内联到<head> */}}
{{ $critical := resources.Get "css/critical.css" | resources.Minify }}
<style>{{ $critical.Content | safeCSS }}</style>
{{/* 2. 非首屏CSS - 预加载但不阻塞渲染 */}}
{{ $normalize := resources.Get "css/libs/normalize.css" }}
{{ $remixicon := resources.Get "css/libs/remixicon.css" }}
{{ $uikit := resources.Get "css/libs/uikit.min.css" }}
{{ $nprogress := resources.Get "css/libs/nprogress.min.css" }}
{{ $message := resources.Get "css/libs/message.min.css" }}
{{ $main := resources.Get "css/main.css" }}
{{/* 合并非关键CSS */}}
{{ $nonCritical := slice $normalize $remixicon $uikit $nprogress $message $main | resources.Concat "css/style.css" | resources.Minify | resources.Fingerprint }}
{{/* 使用preload异步加载,不阻塞渲染 */}}
<link rel="preload" href="{{ $nonCritical.RelPermalink }}" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ $nonCritical.RelPermalink }}"></noscript>
```
---
### 步骤1.3:创建Hugo构建脚本(可选,用于自动化)
**文件路径:** `themes/Ying/scripts/extract-critical-css.sh`
```bash
#!/bin/bash
# 从main.css提取关键CSS的辅助脚本
# 用法:./scripts/extract-critical-css.sh
echo "正在分析main.css..."
# 标记:以下选择器为首屏关键CSS
CRITICAL_SELECTORS=(
":root"
"html"
"body"
".main"
".header"
".header-top"
".header-avatar"
".header-info"
".header-social"
".header-bio"
".header-menu"
"h1"
"h2"
"h3"
"h4"
"h5"
"h6"
"a"
"p"
"ul"
"img"
"hr"
".meta"
".meta-item"
".footer"
".post-title"
".theme-toggle-button"
"@media"
"@font-face"
":root"
"[data-theme"
)
echo "请根据以上选择器手动提取CSS到critical.css"
echo "或使用自动化CSS提取工具如:critical、penthouse"
```
---
## 🧪 测试验证清单
### 测试1:首屏渲染性能
- [ ] 使用Chrome DevTools的Network面板查看:
- 首字节时间(TTFB)是否正常
- 首次内容绘制(FCP)是否提升
- 样式表是否正确加载
### 测试2:视觉一致性
- [ ] 首页显示正常
- [ ] Header布局正确
- [ ] 导航菜单样式正确
- [ ] 头像显示正常
- [ ] 社交链接图标正确
- [ ] 深色模式切换正常
- [ ] 文章列表页显示正常
- [ ] 文章卡片样式正确
- [ ] 分页按钮正常
### 测试3:深色模式
- [ ] 点击头像切换深色模式
- [ ] 所有文字颜色正确
- [ ] 背景颜色正确
- [ ] 阴影效果正确
- [ ] 链接颜色正确
### 测试4:响应式布局
- [ ] 桌面端(>768px)显示正常
- [ ] 移动端(<768px)显示正常
- [ ] 菜单在移动端正确折叠
### 测试5:资源加载
- [ ] 检查Console无错误
- [ ] 所有CSS文件加载成功
- [ ] 字体文件加载成功(Network面板)
- [ ] 无404错误
---
## 📊 预期收益
### 性能指标提升(预估)
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| 首屏CSS大小 | ~500KB | ~20KB(内联) | ⚡ -96% |
| CSS阻塞时间 | ~800ms | ~0ms | ⚡ -100% |
| FCP | ~1.8s | ~1.2s | ⚡ -33% |
| Speed Index | ~2.5s | ~1.8s | ⚡ -28% |
### 用户体验提升
- ✅ 页面立即开始渲染(无需等待CSS下载)
- ✅ 深色模式无闪烁(CSS变量立即生效)
- ✅ 字体加载更平滑(font-display: swap生效)
- ✅ 首屏内容更快可见
---
## ⚠️ 注意事项
### 1. 背景图片的base64数据
当前body的background-image使用了很长的base64数据。这个数据应该:
- 保持在critical.css中(确保首屏背景显示)
- 或者移至单独文件并使用preload
建议:将base64数据移至单独的CSS文件 `background.css`,使用 `fetchpriority="high"` 优先加载
### 2. Remixicon字体文件
`remixicon.css` 引用了字体文件(.woff2, .woff)。确保:
- 字体文件路径正确
- 使用 `font-display: swap` 避免FOIT
### 3. UIkit依赖
UIkit的CSS可能包含一些组件样式(如grid、modal等)。检查:
- 深色模式是否正确覆盖UIkit默认样式
- 是否有样式冲突
### 4. 浏览器兼容性
`<link rel="preload" onload>` 技术需要测试:
- Chrome/Edge:✅ 完全支持
- Firefox:✅ 支持
- Safari:✅ 支持(11.1+)
- IE11:❌ 不支持(需要polyfill或fallback)
---
## 🔄 回滚方案
如果出现问题,快速回滚到原始方案:
```gohtml
{{/* 回滚到原始CSS加载方式 */}}
{{ $normalize := resources.Get "css/libs/normalize.css" }}
{{ $remixicon := resources.Get "css/libs/remixicon.css" }}
{{ $uikit := resources.Get "css/libs/uikit.min.css" }}
{{ $nprogress := resources.Get "css/libs/nprogress.min.css" }}
{{ $message := resources.Get "css/libs/message.min.css" }}
{{ $main := resources.Get "css/main.css" }}
{{ $style := slice $normalize $remixicon $uikit $nprogress $message $main | resources.Concat "css/style.css" | resources.Minify | resources.Fingerprint }}
<link rel="stylesheet" href="{{ $style.RelPermalink }}">
```
---
## 📚 扩展阅读
1. **Critical CSS最佳实践**
- https://web.dev/extract-critical-css/
- https://github.com/addyosmani/critical
2. **CSS加载策略**
- https://web.dev/preload-responsive-stylesheets/
- https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload
3. **性能优化工具**
- Lighthouse:https://developers.google.com/web/tools/lighthouse
- WebPageTest:https://www.webpagetest.org/
---
## ✅ 实施清单
- [ ] 创建 `themes/Ying/assets/css/critical.css`
- [ ] 修改 `themes/Ying/layouts/partials/head.html`
- [ ] 测试首页显示
- [ ] 测试深色模式
- [ ] 测试移动端
- [ ] 使用Lighthouse验证性能提升
- [ ] 提交代码
---
**预计耗时:** 1-2小时
**风险等级:** 低(有完整回滚方案)