705 lines
16 KiB
Markdown
705 lines
16 KiB
Markdown
# 第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小时
|
|||
|
|
**风险等级:** 低(有完整回滚方案)
|