模板与页面数据
使用编译器提供的站点、文章、文档树、分页和归档数据。
BookX 使用 Go 的 html/template。每个布局接收一份页面数据,其中 .Site 是全站信息,.Page 是当前页面信息,其他字段按页面类型提供。不要假定每个页面都有 .Post、.Doc 或 .Pagination。
一个列表布局
在继承 Paper 的主题中,下面可以作为 layouts/list.html,复用父主题的页头、页脚和分页:
{{ define "layout" }}
{{ template "head" . }}
{{ template "header" . }}
<main class="site-frame page-main">
<header class="page-heading">
<h1>{{ .Page.Title }}</h1>
<p>{{ .Page.Description }}</p>
</header>
<ul>
{{ range .Posts }}
<li><a href="{{ .URL }}">{{ .Title }}</a> <time>{{ .DateText }}</time></li>
{{ end }}
</ul>
{{ template "pagination" . }}
</main>
{{ template "footer" . }}
{{ end }}
range 和 with 会改变当前的 .。如果在循环中需要访问页面根数据,使用 $,例如 $.Site.Title。普通标题和文本保留模板自动转义,不要为了方便拼接而转换成不受检查的 HTML。
通用字段
| 字段 | 用途 |
|---|---|
.Site.Title、.Site.Description | 站点名称与介绍 |
.Site.HomeURL、.Site.BlogURL、.Site.ArchiveURL | 已处理部署子路径的导航地址 |
.Site.DocsEnabled、.Site.ProjectsEnabled、.Site.LinksEnabled | 是否展示对应入口 |
.Site.DocCollections | 顶部文档合集列表 |
.Site.AssetsURL | 未合并主题资源的 URL 前缀 |
.Site.BundleCSSURL、.Site.BundleJSURL | 已生成的资源合并地址,未启用时为空 |
.Site.SearchDataURL | 静态搜索索引地址 |
.Page.Title、.Page.Description、.Page.URL | 当前页面标题、描述与地址 |
.Page.Canonical、.Page.Kind | 正式规范地址与页面类型 |
.Year | 构建内容对应的年份,用于页脚等展示 |
这些 URL 字段已经处理站点子路径,不要再手工拼接一次 base_path。也不要把 /assets/theme/ 或 /blog/ 写死到模板中。
文章、首页与分页
列表页的 .Posts 已完成排序与分页,只负责逐项渲染。首页使用 .HomePosts 获取按当前布局分页数量限制的最近文章;首页旧 .Posts 只保留最近 4 篇以兼容旧主题。文章总数使用 .Site.PostCount,不能用首页列表长度代替。
文章详情使用 .Post 获取元信息,.Content 输出已经编译的正文,.Newer 和 .Older 是可选前后文章。.TOC 提供标题的 ID、Text 和 Level。分类和标签链接可使用文章的 .CategoryURL、.TagLinks,无需自己从名称生成 URL。
.Pagination 包含当前页 Current、总页数 Total、PrevURL、NextURL 与 PageURLs。每个页码还包含 Current 和 Ellipsis 状态;省略项不是链接。直接复用父主题 pagination 可保持长页码列表、键盘访问和子路径行为一致。
文档数据
文档列表页使用 .Collections。文档详情的 .Collection 是当前合集,.Doc 是当前页面,.Sidebar 是已经整理好的导航树,.Breadcrumbs 是面包屑。
导航节点的 Children 表示下级,Active 和 Open 表示选中与展开状态。节点有标题但没有 URL 时,渲染为可展开目录或纯文字,不要制造一个不存在的首页链接。面包屑 URL 为空时也应输出文字。
顶级合集保留首页;子目录关闭首页后仍可有下级内容。主题应使用编译器给出的树,不要遍历全部文档后自行重建父子关系。页内目录与左侧文档树也应分别渲染。
归档分批展示
新主题使用 .Archive.Years 渲染当前静态页,每个分组有 Year、全年总数 Count 和本页 Posts。Count 不能换成 len .Posts,因为一个年份可能跨过多批。
归档首页的 .Archive.NextDataURL 指向下一批 JSON;没有更多数据时为空。编译器每批最多输出 50 篇,JSON 中只包含列表字段及下一批地址,主题不需要自己判断年份文件名或拼接 JSON 路径。
共享 archive.js 依赖以下模板标记:
| 标记 | 位置 |
|---|---|
data-archive-groups | 所有年份的容器 |
data-archive-year | 年份区块,值为年份 |
data-archive-id | 每个文章链接,值为稳定内容 ID |
data-archive-load 和 data-next | 加载区及下一批 JSON URL |
data-archive-more、data-archive-status | 加载按钮与状态提示 |
data-archive-pagination | 完成后需要隐藏的静态分页容器 |
脚本还复用 archive-year、archive-year-label、archive-year-posts 等结构类名。若保留共享加载逻辑,建议从 Paper 的 archive.html 开始修改,保证静态与追加内容的结构一致。
失败时保留重试和静态分页;全部加载完成后隐藏按钮与分页,仅显示完成提示。旧 .ArchiveYears 仍是完整列表,供尚未接入分批加载的自定义主题使用。
评论、搜索与 SEO
复用 head、comments 以及搜索相关模板,可以保留规范地址、分享信息、稳定评论关联和按需加载行为。评论是否启用以 .Comments 数据为准,不根据页面类型硬编码开启。
搜索索引由 .Site.SearchDataURL 提供,打开搜索时再请求即可。不要在每个页面初始化时额外下载全部搜索数据;归档也使用自身的精简分片,不用搜索索引冒充归档数据。
完整字段以源码的 internal/bookx/models.go 为准,现有模板位于 themes/paper/。新增页面需求若需要新的内容关系,应先在编译核心建立清晰的数据协议,再由主题渲染。
讨论这篇内容
评论由 GitHub Discussions 提供 · 打开讨论仓库