將Hexo Light主題的Fancybox v3升級至v5並移除jQuery依賴

注意

本文由 Gemini 3.1 Pro 語言模型產生。

為了提升部落格的載入效能與行動裝置的瀏覽體驗,本次將 hexo-theme-light 主題預設的 Fancybox v3 升級至新版的 Fancybox v5(@fancyapps/ui)。

Fancybox v5 最大的優勢在於完全移除了 jQuery 依賴,並且在手機端支援非常順暢的原生手勢(包含雙指捏合與雙擊放大)。趁著這次升級,我們也將主題內依賴 jQuery 的 gallery.js 改寫為純原生 JavaScript(Vanilla JS),進一步達成網站瘦身。

步驟一:下載 Fancybox v5 檔案(本地託管)

為了不依賴外部 CDN,我們將所需的 JS 與 CSS 檔案下載並放置於主題目錄內。

  1. 從以下網址下載最新的 minified 檔案:
  2. 在主題資料夾下建立新目錄並放入檔案:themes/light/source/fancybox5/

檔案結構如下:

└── themes
    └── light
        └── source
            └── fancybox5
                ├── fancybox.min.css
                └── fancybox.umd.min.js

原本的 gallery.js 依賴 jQuery 和 imagesLoaded 套件。我們將其完全使用原生 JS 改寫,並將 Fancybox 的觸發屬性改為 v5 支援的 data-fancyboxdata-caption

開啟 themes/light/source/js/gallery.js,清空原內容並替換為以下程式碼:

document.addEventListener('DOMContentLoaded', function() {
  // === 1. 處理圖說 (Caption) 與 Fancybox 5 的包裝 ===
  const entries = document.querySelectorAll('.entry');

  entries.forEach((entry, i) => {
    const images = entry.querySelectorAll('img');
    images.forEach(img => {
      // 排除帶有 nofancybox class 的圖片
      if (!img.classList.contains('nofancybox')) {
        const alt = img.alt;

        // 建立並插入 Caption
        if (alt) {
          const caption = document.createElement('span');
          caption.className = 'caption';
          caption.textContent = alt;
          img.parentNode.insertBefore(caption, img.nextSibling);
        }

        // 建立 <a> 標籤來包裝圖片 (使用 data-fancybox)
        const wrapper = document.createElement('a');
        wrapper.href = img.src;
        wrapper.setAttribute('data-fancybox', 'gallery' + i);
        if (alt) {
          wrapper.setAttribute('data-caption', alt);
        }

        img.parentNode.insertBefore(wrapper, img);
        wrapper.appendChild(img);
      }
    });
  });

  // === 2. 處理畫廊 (Gallery) 的播放與原生動畫 ===
  const play = (parent, item, callback) => {
    const width = parent.clientWidth;
    const imgElement = item.tagName.toLowerCase() === 'img' ? item : item.querySelector('img');

    if (!imgElement) return;

    const executeAnimation = () => {
      const nWidth = imgElement.naturalWidth || imgElement.width;
      const nHeight = imgElement.naturalHeight || imgElement.height;

      callback();

      item.style.transition = 'opacity 0.5s ease';
      item.style.opacity = '1';

      parent.style.transition = 'height 0.5s ease';
      parent.style.height = (width * nHeight / nWidth) + 'px';
    };

    // 原生判斷圖片是否已載入
    if (imgElement.complete) {
      executeAnimation();
    } else {
      imgElement.addEventListener('load', executeAnimation);
    }
  };

  const galleries = document.querySelectorAll('.gallery');
  galleries.forEach(gallery => {
    let current = 0;
    const photosetContainer = gallery.querySelector('.photoset');

    if (!photosetContainer) return;

    const photoset = Array.from(photosetContainer.children);
    const all = photoset.length;
    let loading = true;

    if (all === 0) return;

    play(gallery, photoset[0], () => {
      loading = false;
    });

    gallery.addEventListener('click', function(e) {
      if (e.target.closest('.prev')) {
        if (!loading) {
          const next = (current - 1 + all) % all;
          loading = true;

          play(gallery, photoset[next], () => {
            photoset[current].style.transition = 'opacity 0.5s ease';
            photoset[current].style.opacity = '0';
            loading = false;
            current = next;
          });
        }
      } else if (e.target.closest('.next')) {
        if (!loading) {
          const next = (current + 1) % all;
          loading = true;

          play(gallery, photoset[next], () => {
            photoset[current].style.transition = 'opacity 0.5s ease';
            photoset[current].style.opacity = '0';
            loading = false;
            current = next;
          });
        }
      }
    });
  });
});

現在我們不再需要 jQuery 和舊版的 Fancybox,可以將它們從頁面載入區塊中移除,並補上 Fancybox v5 的初始化設定。

開啟 themes/light/layout/_partial/after_footer.ejs,修改為以下內容:

<%- js('js/gallery.js') %>
<%- js('js/highlight.min.js') %>
<script>hljs.highlightAll();</script>
<%- js('js/clipboard.min.js') %>
<%- js('js/clipboard.use.js') %>

<% if (theme.fancybox5){ %>
<%- css('fancybox5/fancybox.min.css') %>
<%- js('fancybox5/fancybox.umd.min.js') %>
<script>
  document.addEventListener("DOMContentLoaded", function() {
    Fancybox.bind("[data-fancybox]", {
      Hash: false,
      Toolbar: {
        display: {
          left: ["infobar"],
          middle: [
            "zoomIn",
            "zoomOut",
            "toggle1to1",
            "rotateCCW",
            "rotateCW",
            "flipX",
            "flipY",
          ],
          right: ["slideshow", "thumbs", "close"],
        },
      },
    });
  });
</script>
<% } %>

步驟四:修改主題配置檔(_config.yml

開啟 themes/light/_config.yml,啟用新的 fancybox5 變數並停用舊版:

# Lightbox
fancybox5: true
# fancybox: false

步驟五:刪除冗餘檔案與重新生成

確保環境乾淨,將以下不再使用的歷史檔案與資料夾直接刪除:

  • 刪除目錄:themes/light/source/fancybox/(舊版燈箱)
  • 刪除檔案:themes/light/source/js/jquery-3.7.1.min.js
  • 刪除檔案:themes/light/source/js/jquery.imagesloaded.min.js

最後,在終端機執行清除快取與重新生成指令:

hexo clean
hexo g
hexo s

升級完成!現在部落格不僅載入速度更快,行動裝置的圖片瀏覽體驗也獲得了大幅度的提升。