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

390 lines
12 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.
# 第2步:JS按需加载优化 - 最终实施方案
## 📊 优化策略总结
### 核心原则
1. **UIkit始终加载** - 作为基础框架,不按需加载
2. **核心功能始终加载** - 导航、搜索、主题切换等
3. **页面特定功能按需加载** - 评论、打赏、无限滚动等
4. **延迟加载非关键功能** - Toast、懒加载等
---
## 🔧 实施代码
### 修改footer.html(第50-133行)
**删除原有代码,替换为:**
```gohtml
{{/* ====== 优化后的JS加载策略 ====== */}}
{{/* ====== 1. 核心JS Bundle - 始终加载 ====== */}}
{{ $iconfont := resources.Get "js/libs/iconfont.js" }}
{{ $uikit := resources.Get "js/libs/uikit.min.js" }}
{{ $viewimage := resources.Get "js/libs/view-image.min.js" }}
{{ $utils := resources.Get "js/modules/utils.js" }}
{{ $loader := resources.Get "js/modules/loader.js" }}
{{ $cache := resources.Get "js/modules/cache.js" }}
{{ $search := resources.Get "js/modules/search.js" }}
{{ $floatingTools := resources.Get "js/modules/floating-tools.js" }}
{{ $nprogress := resources.Get "js/libs/nprogress.js" }}
{{ $pjaxLib := resources.Get "js/libs/pjax.js" }}
{{ $mypjax := resources.Get "js/modules/mypjax.js" }}
{{ $pangu := resources.Get "js/modules/pangu.js" }}
{{ $linkify := resources.Get "js/modules/linkify.js" }}
{{ $main := resources.Get "js/main.js" }}
{{ $coreScripts := slice $iconfont $uikit $viewimage $utils $loader $cache $search $floatingTools $nprogress $pjaxLib $mypjax $pangu $linkify $main | resources.Concat "js/core.js" | resources.Minify | resources.Fingerprint }}
<script defer src="{{ $coreScripts.RelPermalink }}"></script>
{{/* ====== 2. 配置变量 - 必须在核心JS之前或同时 ====== */}}
<script>
window.enableInfiniteScroll = {{ .Site.Params.infiniteScroll.enable | default false }};
</script>
{{/* ====== 3. 远程数据获取 - 构建时 ====== */}}
{{/* Fetch friend data at build time — no runtime network requests */}}
{{ $linkHosts := slice }}
{{ $linkNames := slice }}
{{ with resources.GetRemote "https://api.usj.cc/api/links?all=1" }}
{{ with .Err }}
{{ warnf "Links API fetch failed: %s" . }}
{{ else }}
{{ with .Content | transform.Unmarshal }}
{{ range .links }}
{{ $host := "" }}
{{ with .url | urls.Parse }}
{{ $host = .Host | strings.TrimPrefix "www." }}
{{ end }}
{{ $title := .name | default "" }}
{{ if $host }}
{{ $linkHosts = $linkHosts | append (dict "host" $host "title" $title) }}
{{ end }}
{{ if $title }}
{{ $linkNames = $linkNames | append (dict "lower" (lower $title) "title" $title) }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
<script>
window.friendLinks = {{ dict "hosts" $linkHosts "names" $linkNames | jsonify | safeJS }};
</script>
{{ $feedHosts := slice }}
{{ $feedNames := slice }}
{{ with resources.GetRemote "https://api.usj.cc/api/feeds" }}
{{ with .Err }}
{{ warnf "Feeds API fetch failed: %s" . }}
{{ else }}
{{ with .Content | transform.Unmarshal }}
{{ range .feeds }}
{{ $host := "" }}
{{ with .url | urls.Parse }}
{{ $host = .Host | strings.TrimPrefix "www." }}
{{ end }}
{{ $title := .feedTitle | default "" }}
{{ if $host }}
{{ $feedHosts = $feedHosts | append (dict "host" $host "title" $title) }}
{{ end }}
{{ if $title }}
{{ $feedNames = $feedNames | append (dict "lower" (lower $title) "title" $title) }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
<script>
window.friendFeeds = {{ dict "hosts" $feedHosts "names" $feedNames | jsonify | safeJS }};
</script>
{{/* ====== 4. 页面特定JS - 按需加载 ====== */}}
{{/* 文章详情页专用JS */}}
{{ if .IsPage }}
{{ $artalkModule := resources.Get "js/modules/artalk.js" }}
{{ $paragraphComments := resources.Get "js/modules/paragraph-comments.js" }}
{{ $reward := resources.Get "js/modules/reward.js" }}
{{ $pageScripts := slice $artalkModule $paragraphComments $reward | resources.Concat "js/page-only.js" | resources.Minify | resources.Fingerprint }}
<script defer src="{{ $pageScripts.RelPermalink }}"></script>
{{ end }}
{{/* 首页专用JS */}}
{{ if .IsHome }}
{{ if .Site.Params.infiniteScroll.enable }}
{{ $infiniteScroll := resources.Get "js/modules/infinite-scroll.js" }}
<script defer src="{{ $infiniteScroll.RelPermalink }}"></script>
{{ end }}
{{ end }}
{{/* 特定页面专用JS */}}
{{ if or (eq .Type "links") (eq .Type "circles") }}
{{ $tiaozhuan := resources.Get "js/modules/tiaozhuan.js" }}
<script defer src="{{ $tiaozhuan.RelPermalink }}"></script>
{{ end }}
{{/* ====== 5. 延迟加载的非关键JS ====== */}}
{{ $toast := resources.Get "js/modules/toast.js" }}
{{ $easylazyload := resources.Get "js/modules/lazyload.js" }}
{{ $deferredScripts := slice $toast $easylazyload | resources.Concat "js/deferred.js" | resources.Minify | resources.Fingerprint }}
<script>
// 使用requestIdleCallback在浏览器空闲时加载
if ('requestIdleCallback' in window) {
requestIdleCallback(function() {
var script = document.createElement('script');
script.src = '{{ $deferredScripts.RelPermalink }}';
script.defer = true;
document.body.appendChild(script);
});
} else {
setTimeout(function() {
var script = document.createElement('script');
script.src = '{{ $deferredScripts.RelPermalink }}';
script.defer = true;
document.body.appendChild(script);
}, 1000);
}
</script>
{{/* ====== 6. 第三方脚本 ====== */}}
<script async src="/js/bsz.js"></script>
```
---
## 📦 Bundle拆分详情
### Core.js(核心bundle)
**包含:** ~14个文件
**大小:** ~200KB(压缩后)
**加载策略:** defer(DOM解析完成后立即执行)
**包含模块:**
- UIkit框架
- 图标字体
- 图片灯箱
- 工具函数
- 加载器
- 缓存
- 搜索
- 浮动工具
- 进度条
- PJAX
- 中英文空格处理
- 链接处理
- 主题主逻辑
### Page-only.js(文章详情页专用)
**包含:** 3个文件
**大小:** ~180KB(压缩后)
**加载策略:** defer(仅在文章详情页加载)
**包含模块:**
- Artalk评论系统
- 段落评论
- 打赏功能
### Deferred.js(延迟加载)
**包含:** 2个文件
**大小:** ~25KB(压缩后)
**加载策略:** requestIdleCallback(浏览器空闲时加载)
**包含模块:**
- Toast消息
- 图片懒加载
### 独立加载的JS
- infinite-scroll.js - 仅首页(如果启用)
- tiaozhuan.js - 仅links/circles页面
- bsz.js - 异步加载(不蒜子计数)
---
## 🎯 优化效果预估
### 首页加载
```
优化前:
HTML ████████░░░░░░░░░░
CSS ██████░░░░░░░░░░░░ (内联)
JS ██████████████████ (800KB,全部加载)
渲染 ████████████░░░░░░
优化后:
HTML ████████░░░░░░░░░░
CSS ██████░░░░░░░░░░░░ (内联)
JS ██████████░░░░░░░░ (350KB,核心+条件)
空闲 ░░░░░░░░░░████░░░░ (25KB,延迟加载)
渲染 ████████████████░░ (提前!)
```
### 文章详情页加载
```
优化前:
JS ██████████████████ (800KB)
渲染 ████████████░░░░░░
优化后:
JS ████████████░░░░░░ (450KB)
渲染 ████████████████░░ (提前!)
评论 ░░░░░░░░░░████████ (Artalk按需加载)
```
---
## 🧪 功能测试清单
### 必测项目
#### 1. 首页功能
- [ ] 导航菜单点击正常
- [ ] 搜索框打开/关闭正常
- [ ] 搜索结果显示正常
- [ ] 主题切换(深色/浅色)正常
- [ ] 文章列表正常显示
- [ ] 无限滚动加载正常(如果启用)
- [ ] 浮动工具栏正常(搜索、主题、返回顶部)
#### 2. 文章详情页功能
- [ ] 文章内容正常显示
- [ ] 图片灯箱正常(点击查看大图)
- [ ] Artalk评论区正常加载
- [ ] 评论功能正常(发布、回复)
- [ ] 打赏按钮功能正常
- [ ] 段落评论功能正常(如果启用)
- [ ] 返回顶部按钮正常
#### 3. 其他页面功能
- [ ] 友链页面跳转正常
- [ ] circles页面功能正常
- [ ] 归档页面正常
- [ ] 搜索结果页正常
#### 4. 跨页面功能
- [ ] PJAX导航正常(页面无刷新切换)
- [ ] 浏览器前进/后退正常
- [ ] 书签/分享链接正常
### 性能测试
#### Chrome DevTools测试
**Network面板:**
1. 打开DevTools → Network
2. 刷新页面
3. 检查:
- [ ] core.js首先加载
- [ ] page-only.js仅在文章页加载
- [ ] deferred.js最后加载
- [ ] 总体JS大小减少
**Performance面板:**
1. 记录页面加载
2. 检查:
- [ ] TBT(Total Blocking Time)减少
- [ ] 长任务数量减少
- [ ] 交互响应更快
**Lighthouse:**
1. 运行Performance审计
2. 预期指标:
- [ ] Performance得分:90+
- [ ] TTI:改善20-30%
- [ ] TBT:改善40-50%
- [ ] Speed Index:改善20-30%
---
## 🐛 故障排除
### 问题1:评论区未加载
**症状:** 文章详情页看不到评论区
**可能原因:**
1. page-only.js加载失败
2. Artalk初始化时机不对
3. JavaScript错误
**解决方案:**
1. 打开Console查看错误
2. 检查Network面板,确认page-only.js加载成功
3. 确认artalkConfig存在(head.html中定义)
### 问题2:功能延迟初始化
**症状:** 点击某些按钮无响应,过一会儿才正常
**原因:** 模块还未加载完成就尝试调用
**解决方案:**
- 这是预期行为,用户可能会感觉到轻微延迟
- 如果延迟明显,考虑将该模块移到core.js中
### 问题3:无限滚动失效
**症状:** 首页无法加载更多文章
**可能原因:**
1. infinite-scroll.js未加载
2. window.enableInfiniteScroll未定义
**解决方案:**
1. 确认Site.Params.infiniteScroll.enable为true
2. 检查Console是否有错误
3. 确认infinite-scroll.js加载成功
---
## 🔄 回滚方案
如果优化后出现严重问题,快速回滚:
```gohtml
{{/* 恢复原始JS加载方式 */}}
{{ $iconfont := resources.Get "js/libs/iconfont.js" }}
{{ $uikit := resources.Get "js/libs/uikit.min.js" }}
{{ $easylazyload := resources.Get "js/modules/lazyload.js" }}
{{ $viewimage := resources.Get "js/libs/view-image.min.js" }}
{{ $main := resources.Get "js/main.js" }}
{{ $mypjax := resources.Get "js/modules/mypjax.js" }}
{{ $nprogress := resources.Get "js/libs/nprogress.js" }}
{{ $tiaozhuan := resources.Get "js/modules/tiaozhuan.js" }}
{{ $toast := resources.Get "js/modules/toast.js" }}
{{ $reward := resources.Get "js/modules/reward.js" }}
{{/* Modules */}}
{{ $utils := resources.Get "js/modules/utils.js" }}
{{ $loader := resources.Get "js/modules/loader.js" }}
{{ $cache := resources.Get "js/modules/cache.js" }}
{{ $search := resources.Get "js/modules/search.js" }}
{{ $floatingTools := resources.Get "js/modules/floating-tools.js" }}
{{ $paragraphComments := resources.Get "js/modules/paragraph-comments.js" }}
{{ $infiniteScroll := resources.Get "js/modules/infinite-scroll.js" }}
{{ $artalkModule := resources.Get "js/modules/artalk.js" }}
{{ $pjaxLib := resources.Get "js/libs/pjax.js" }}
{{ $linkify := resources.Get "js/modules/linkify.js" }}
{{ $pangu := resources.Get "js/modules/pangu.js" }}
{{ $scripts := slice $iconfont $uikit $easylazyload $viewimage $utils $loader $cache $search $floatingTools $paragraphComments $infiniteScroll $artalkModule $nprogress $pjaxLib $pangu $linkify $main $mypjax $toast $tiaozhuan $reward | resources.Concat "js/bundle.js" | resources.Minify | resources.Fingerprint }}
<script defer src="{{ $scripts.RelPermalink }}"></script>
<script async src="/js/bsz.js"></script>
```
---
## ✅ 完成确认
测试通过后,第2步即完成。继续第3步:**字体子集化优化**
---
**文档版本:** v2.0
**最后更新:** 2026-06-03