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

12 KiB
Raw Blame History

第2步:JS按需加载优化 - 最终实施方案

📊 优化策略总结

核心原则

  1. UIkit始终加载 - 作为基础框架,不按需加载
  2. 核心功能始终加载 - 导航、搜索、主题切换等
  3. 页面特定功能按需加载 - 评论、打赏、无限滚动等
  4. 延迟加载非关键功能 - Toast、懒加载等

🔧 实施代码

修改footer.html(第50-133行)

删除原有代码,替换为:

{{/* ====== 优化后的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加载成功

🔄 回滚方案

如果优化后出现严重问题,快速回滚:

{{/* 恢复原始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