# 背景說明

Shoka 主題已經停止更新許久,但內部有許多寫死的邏輯,特別是圖片處理部分。原本的設計是使用新浪圖床(Sina Image Server),但隨著時間推移,我們可能想要:

  1. 使用自己的 CDN 服務(如 Cloudflare R2)來托管隨機背景圖
  2. 保留某些本地資源(如 favicon、avatar 等)在網站本地

# 問題分析

# 原本的限制

  1. 寫死的新浪圖床邏輯:在 themes/shoka/scripts/helpers/engine.js 中, parseImage 函數會自動將圖片路徑轉換為新浪圖床格式
  2. 無法區分本地與遠端資源:所有非完整 URL 的圖片都會被當作新浪圖床處理
  3. 配置不靈活: _config.shoka.yml 中的 images 設定無法同時支援 CDN 和本地資源

# 解決方案

# 核心概念

通過修改圖片處理邏輯,實現:

  • 本地資源( images/ 路徑)→ 使用網站本地路徑
  • CDN 資源(數字檔名)→ 使用自訂 CDN(如 R2)
  • 完整 URL → 直接使用原始 URL

# 修改步驟

# 1. 修改配置檔 _config.shoka.yml

# 第 14 行:將 images 改為你的 CDN URL
images: https://pub-da440be71028438aa9683b63bef5ccf1.r2.dev
# 第 18 行:本地資源使用 images/ 前綴
favicon:
  apple_touch_icon: images/apple-touch-icon.png
# 第 86 行:頭像也使用 images/ 前綴
sidebar:
  avatar: images/avatar.jpg
# 第 107-109 行:支付二維碼也使用 images/ 前綴
reward:
  account:
    wechatpay: images/wechatpay.png
    alipay: images/alipay.png
    paypal: images/paypal.png

關鍵邏輯:

  • 包含 images/ 的路徑 = 本地資源
  • 不包含 images/ 的路徑 = CDN 資源

# 2. 修改圖片列表 themes/shoka/_images.yml

將原本複雜的新浪圖床檔名(如 6833939bly1giciryrr3rj20zk0m8nhk.jpg )改為簡單的數字編號:

- 1.jpg
- 2.jpg
- 3.jpg
# ...
- 100.jpg

注意:這些檔案需要上傳到你的 CDN(R2)根目錄,檔名必須與列表一致。


# 3. 修改 themes/shoka/scripts/helpers/engine.js

# 修改 1: parseImage 函數(第 27-41 行)

var parseImage = function(img, size) {
  if (img.startsWith('//') || img.startsWith('http')) {
    return img;
  }
  // 新增這一段:如果路徑包含 images/,代表它是本地 source 裡的靜態資源
  else if (img.startsWith('images/') || img.startsWith('/images/')) {
    const { statics } = hexo.theme.config;
    return (statics || '/') + img;
  }
  else {
    const config = hexo.theme.config;
    const baseUrl = config.images.endsWith('/') ? config.images : config.images + '/';
    return baseUrl + img;
  }
}

說明:

  • 第 1 個 if :完整 URL 直接返回
  • 第 2 個 else if :包含 images/ 的路徑,使用本地 statics 路徑
  • 第 3 個 else :其他路徑(如 1.jpg ),使用 CDN 路徑

# 修改 2: _image_url helper(第 118-140 行)

hexo.extend.helper.register('_image_url', function(img, path = '') {
  if (!img) return '';
  const config = hexo.theme.config;
  // 1. 如果已經是完整網址,直接返回
  if (img.startsWith('//') || img.startsWith('http')) {
    return img;
  }
  // 2. 處理本地資源 (頭像、favicon 等)
  // 如果路徑包含 images/ 且不是純數字 ID,我們直接返回本地路徑,不經過 url_for
  if (img.includes('images/')) {
    // 確保路徑是以 / 開頭的絕對路徑,例如 /images/avatar.jpg
    return img.startsWith('/') ? img : '/' + img;
  }
  // 3. 處理 R2 隨機封面圖
  // 確保 baseUrl 最後有斜線,且不讓 Hexo 自動處理它
  const baseUrl = config.images.endsWith('/') ? config.images : config.images + '/';
  return baseUrl + img;
});

重要:

  • 本地資源返回 /images/xxx.jpg 格式
  • CDN 資源返回 https://your-cdn.com/xxx.jpg 格式
  • 不使用 url_for() ,避免 Hexo 自動處理路徑

# 4. 修改模板 themes/shoka/layout/_partials/sidebar/overview.njk

<!-- 第 2 行:使用 _image_url() 函數處理頭像 -->
<img class="image" itemprop="image" alt="{{ author }}"
    data-src="{{ _image_url(theme.sidebar.avatar) }}">

說明:原本直接拼接 URL,現在統一使用 _image_url() 函數處理,確保邏輯一致。


# 文件結構

修改完成後,你的文件結構應該如下:

Blog/
├── _config.shoka.yml          # 主配置檔
├── themes/shoka/
│   ├── _images.yml            # 隨機圖片列表(1.jpg ~ 100.jpg)
│   ├── source/images/         # 本地圖片資源
│   │   ├── avatar.jpg         # 頭像
│   │   ├── favicon.ico        # 網站圖標
│   │   ├── apple-touch-icon.png
│   │   ├── wechatpay.png      # 支付二維碼(如需要)
│   │   └── alipay.png
│   ├── scripts/helpers/
│   │   └── engine.js          # 圖片處理邏輯
│   └── layout/_partials/sidebar/
│       └── overview.njk       # 側邊欄模板

# CDN 端設置

如果使用 Cloudflare R2,需要:

  1. 上傳隨機背景圖:將 1.jpg ~ 100.jpg 上傳到 R2 bucket 根目錄
  2. 設置 Public Access:確保這些圖片可以公開訪問
  3. 設置 Custom Domain(可選):綁定自訂域名,如 https://images.yourdomain.com

# 測試驗證

# 1. 檢查本地資源

打開瀏覽器開發者工具,檢查以下元素的圖片路徑:

<!-- 頭像應該是本地路徑 -->
<img data-src="/images/avatar.jpg">
<!-- Favicon 應該是本地路徑 -->
<link rel="icon" href="/images/favicon.ico">

# 2. 檢查 CDN 資源

檢查文章封面圖或隨機背景圖:

<!-- 隨機背景圖應該是 CDN 路徑 -->
<div style="background-image: url('https://pub-xxx.r2.dev/42.jpg')">

# 3. 清除快取

hexo clean
hexo generate
hexo server

# 常見問題

# Q1: 為什麼頭像還是顯示舊的路徑?

A: 檢查以下幾點:

  1. _config.shoka.yml 中 sidebar.avatar 是否有 images/ 前綴
  2. 確認 themes/shoka/source/images/avatar.jpg 檔案存在
  3. 執行 hexo clean 清除快取

# Q2: 隨機背景圖無法載入?

A: 檢查:

  1. R2 bucket 中的圖片檔名是否與 _images.yml 一致
  2. R2 bucket 是否設置為 Public Access
  3. _config.shoka.yml 中的 CDN URL 是否正確(結尾不需要斜線)

# Q3: 如何新增更多隨機背景圖?

A:

  1. 將新圖片上傳到 R2(如 101.jpg )
  2. 在 _images.yml 中新增 - 101.jpg
  3. 重新生成網站 hexo clean && hexo g

# 總結

這次修改的核心是區分本地資源和 CDN 資源:

資源類型 路徑格式 最終輸出
本地資源 images/avatar.jpg /images/avatar.jpg
CDN 資源 42.jpg https://cdn.com/42.jpg
完整 URL https://example.com/pic.jpg https://example.com/pic.jpg

關鍵文件:

  1. _config.shoka.yml - 配置 CDN URL 和本地資源路徑
  2. engine.js - 實現圖片路徑處理邏輯
  3. _images.yml - 管理隨機背景圖列表

下次遇到類似問題,只需要:

  1. 修改 _config.shoka.yml 的 images 設定
  2. 確保 engine.js 中的 parseImage 和 _image_url 邏輯正確
  3. 區分本地資源(加 images/ 前綴)和 CDN 資源(不加前綴)