主题开发文档

博客系统 v1.0+ 支持完全自定义的主题系统

概述

主题系统允许开发者创建完全自定义的博客外观和布局。每个主题是一个独立的目录,包含配置文件、模板文件和静态资源。主题拥有完全的自主权——自行管理 CSS 样式、布局结构和静态资源,系统不做强制约束。

目录结构

themes/
└── my-theme/
    ├── manifest.json       # 主题配置(必需)
    ├── layouts/            # 布局模板(必需)
    │   └── main.hbs        # 主布局(必需,上传主题时强制验证)
    ├── pages/              # 页面模板(可选,没有则使用默认主题的页面)
    │   ├── index.hbs       # 首页
    │   ├── post.hbs        # 文章详情
    │   ├── category.hbs    # 分类页
    │   ├── tag.hbs         # 标签页
    │   ├── 404.hbs         # 404 页面
    │   └── error.hbs       # 错误页
    ├── partials/           # 片段模板(可选)
    │   └── sidebar.hbs
    └── public/             # 静态资源(可选,CSS/JS/图片等)
        ├── css/
        │   └── style.css
        ├── js/
        └── images/

文件要求

文件/目录 必需性 说明
manifest.json 必需 主题配置文件
layouts/main.hbs 必需 主布局,上传主题时强制验证,没有则拒绝安装
pages/ 可选 页面模板,没有的页面会自动使用默认主题的同名页面
partials/ 可选 片段模板,不支持 fallback
public/ 可选 静态资源,主题可自由组织目录结构

manifest.json

主题的配置文件,定义主题的基本信息和能力:

{
  "name": "my-theme",
  "version": "1.0.0",
  "description": "我的自定义主题",
  "author": "作者名",
  "hooks": [
    "head_css",
    "after_header",
    "before_nav_end",
    "before_content",
    "after_content",
    "after_post_content",
    "sidebar_top",
    "sidebar_bottom",
    "before_footer",
    "body_js"
  ]
}

字段说明

字段 类型 必需 说明
name string 主题唯一名称(目录名)
version string 版本号
description string 主题描述
author string 作者
hooks string[] 声明支持的 Hook 注入点

样式管理

主题完全自主管理自己的 CSS 文件,可以放在主题目录的任意位置。在 layouts/main.hbs 中通过 <link> 标签引入即可。

引入方式

<head>
  <link rel="stylesheet" href="/themes/my-theme/css/style.css">
  <!-- 或任何主题自定义的路径 -->
</head>

推荐的静态资源目录结构

themes/my-theme/public/
├── css/
│   └── style.css
├── js/
│   └── main.js
└── images/
    └── logo.png

静态文件通过 /themes/主题名/ 路径访问,例如 /themes/my-theme/css/style.css

布局模板 (layouts/main.hbs)

主布局文件定义了页面的整体结构,每个主题必须提供自己的 layouts/main.hbs,不支持 fallback。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{title}} - {{../siteName}}</title>
  <link rel="stylesheet" href="/themes/{{../themeName}}/css/style.css">

  {{#pluginHook "head_css"}}{{/pluginHook}}
</head>
<body>
  <header>
    <nav>
      <a href="/">{{../siteName}}</a>
      <ul>
        {{#pluginHook "before_nav_end"}}{{/pluginHook}}
      </ul>
    </nav>

    {{#pluginHook "after_header"}}{{/pluginHook}}
  </header>

  {{#pluginHook "before_content"}}{{/pluginHook}}

  <main>
    {{{body}}}
  </main>

  {{#pluginHook "after_content"}}{{/pluginHook}}

  {{#pluginHook "before_footer"}}{{/pluginHook}}

  <footer>
    <p>&copy; {{currentYear}} {{../siteName}}</p>
    {{#if ../icpNumber}}
      <p>ICP备案: {{../icpNumber}}</p>
    {{/if}}
  </footer>

  {{{pluginScriptTags}}}

  {{#pluginHook "body_js"}}{{/pluginHook}}
</body>
</html>

重要变量

在布局中可通过 ../ 访问全局变量:

变量 说明
siteName 站点名称
siteDescription 站点描述
themeName 当前主题名称
icpNumber ICP备案号
pluginHookData 插件注入内容
pluginScriptTags 插件脚本注入标签
plugins 激活的插件信息
activePlugins 激活的插件列表

页面模板 (pages/)

页面模板是可选的。如果主题没有提供某个页面,系统会自动使用默认主题的对应页面,并配合当前主题的 layouts/main.hbs 渲染。

可用的页面模板

页面文件 说明 Fallback
pages/index.hbs 首页 支持
pages/post.hbs 文章详情 支持
pages/category.hbs 分类页 支持
pages/tag.hbs 标签页 支持
pages/404.hbs 404 页面 支持
pages/error.hbs 错误页面 支持

首页 (index.hbs)

文章列表页面:

<div class="container">
  <aside class="sidebar">
    {{#pluginHook "sidebar_top"}}{{/pluginHook}}

    <div class="categories">
      <h3>分类</h3>
      <ul>
        <li>
          <a href="/" class="{{#unless currentCategory}}active{{/unless}}">
            最近更新
          </a>
        </li>
        {{#each categories}}
          <li>
            <a href="/category/{{slug}}"
               class="{{#if (eq ../currentCategory.id id)}}active{{/if}}">
              {{name}} ({{postCount}})
            </a>
          </li>
        {{/each}}
      </ul>
    </div>

    {{#pluginHook "sidebar_bottom"}}{{/pluginHook}}
  </aside>

  <main class="posts">
    {{#each posts}}
      <article class="post-card">
        <h2><a href="/post/{{slug}}">{{title}}</a></h2>
        <div class="meta">
          <span>{{formatDate updatedAt}}</span>
          {{#each tags}}
            <a href="/tag/{{slug}}" class="tag">{{name}}</a>
          {{/each}}
        </div>
        <p>{{excerpt}}</p>
      </article>
    {{/each}}

    {{#if pagination.hasPrev}}
      <a href="?page={{pagination.prev}}" class="pagination">上一页</a>
    {{/if}}
    {{#if pagination.hasNext}}
      <a href="?page={{pagination.next}}" class="pagination">下一页</a>
    {{/if}}
  </main>
</div>

文章页 (post.hbs)

文章详情页面:

<article class="post-detail">
  <h1>{{post.title}}</h1>

  <div class="meta">
    <span>{{formatDate post.updatedAt}}</span>
    {{#each post.tags}}
      <a href="/tag/{{slug}}" class="tag">{{name}}</a>
    {{/each}}
  </div>

  <div class="content">
    {{{post.htmlContent}}}
  </div>

  {{#pluginHook "after_post_content"}}{{/pluginHook}}
</article>

可用变量

首页/分类/标签页变量:

变量 类型 说明
title string 页面标题
posts array 文章列表
categories array 分类列表(含文章数)
currentCategory object/null 当前分类
pagination object 分页信息
siteName string 站点名称

文章页变量:

变量 类型 说明
title string 文章标题
post object 文章对象
post.title string 文章标题
post.content string 原始 Markdown
post.htmlContent string 渲染后的 HTML
post.excerpt string 文章摘要
post.slug string URL slug
post.createdAt date 创建时间
post.updatedAt date 更新时间
post.author object 作者信息
post.tags array 标签列表
post.category object 分类信息

Fallback 机制

系统对不同类型的模板文件采用不同的 fallback 策略:

资源类型 Fallback 行为
layouts/main.hbs 不支持 fallback,主题必须提供,上传时强制验证
partials/*.hbs 不支持 fallback,主题必须自行提供
pages/*.hbs 支持 fallback,主题没有则使用默认主题的页面

当页面使用默认主题的 page 模板时,布局仍然使用当前主题的 layouts/main.hbs

Hook 注入点

主题可以在以下位置声明 Hook,插件内容会自动注入:

Hook 名称 位置 说明
head_css <head> 注入 CSS 链接
after_header <header> 紧跟 header 后
before_nav_end 导航内部末尾 导航栏末尾
before_content <main> 主内容区之前
after_content <main> 主内容区之后
after_post_content 文章内容后 文章正文之后
sidebar_top 侧边栏顶部 侧边栏开始处
sidebar_bottom 侧边栏底部 侧边栏结束处
before_footer <footer> Footer 之前
body_js <body> 末尾 注入 JS 脚本

Helper 函数

模板中可用的辅助函数:

函数 说明 示例
eq 相等判断 {{#if (eq a b)}}
includes 包含判断 {{#if (includes arr val)}}
add 加法 {{add page 1}}
subtract 减法 {{subtract total 1}}
formatDate 日期格式化 {{formatDate date}}
currentYear 当前年份 {{currentYear}}
json JSON 序列化 {{json data}}
pluginHook 插件内容注入 {{#pluginHook "hookName"}}{{/pluginHook}}

安装主题

方式一:直接放置

  1. 将主题目录复制到 themes/ 目录
  2. 确保目录名与 manifest.json 中的 name 一致
  3. 确保主题包含 layouts/main.hbs 文件
  4. 在后台管理页面激活主题

方式二:上传安装

  1. 进入 后台管理 → 主题管理
  2. 点击「上传主题」按钮
  3. 选择主题 ZIP 包(包含完整主题目录)
  4. 系统自动解压并验证 manifest.jsonlayouts/main.hbs
  5. 如果缺少必要文件,安装将失败并提示具体原因
  6. 验证通过后自动安装,然后激活主题

上传验证规则

验证项 要求 验证时机
manifest.json 必须存在 解压后
layouts/main.hbs 必须存在 解压后、保存前

开发示例

参考默认主题:themes/default/