主题是站点的皮肤:一组 .html 模板文件(fy 标签语法)+ 静态资源(css/js/images)。标签语法详见模板标签参考。
目录结构
public/themes/{主题名}/
├── index.html 首页
├── list.html 栏目列表页
├── content.html 内容详情页
├── page.html 单页(根路径独立单页内容)
├── topic.html 专题单页栏目页(可选,作为栏目的列表页模板使用)
├── search.html 搜索页
├── 404.html 未找到页面
├── css/ 样式
└── images/ 图片模板解析规则
一个页面请求最终用哪个模板,由前台路由决定,与栏目的归属分组(列表/专题)无关:
| 前台路由 | 使用的模板 | 说明 |
|---|---|---|
/ | index.html | 首页 |
/{栏目slug}、/{栏目slug}/page-{n}.html | 栏目设置的「列表页模板」,留空默认 list.html | 栏目页(含分页);归属分组为「专题」的单页栏目同样走此路由、用列表页模板 |
/{栏目slug}/{内容slug}.html | 栏目设置的「详情页模板」,留空默认 content.html | 内容详情页 |
/{单页slug} | page.html | 根路径独立单页内容(不归属任何栏目,如「关于我们」) |
/search | search.html | 搜索页 |
| 其余 | 404.html | 未找到页面 |
两点容易混淆:
- 专题(一栏目一单页)栏目的入口页用的是「列表页模板」,不是
page.html,也不是详情页模板。单页正文靠模板内{fy:list num="1"}取出该栏目唯一内容后用[list:body raw=1]输出,默认主题的topic.html即按此写法实现; page.html只服务不归属任何栏目的独立单页内容,与专题栏目页是两回事。
动态访问与静态化构建(fy:build)走同一路由与模板,行为一致。
模板编译
.html 模板由 fy 编译器编译为 Blade(存于 storage/app/fy/themes/):
- 本地环境:每次请求自动检查 mtime 重编译
- 生产环境:执行
php artisan fy:compile
基本骨架(index.html 示例)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{fy:pagetitle}</title>
{fy:seohead}
<link rel="stylesheet" href="{fy:themepath}/css/style.css">
</head>
<body>
<header>
<a href="/">{fy:sitename}</a>
{fy:nav}
<li class="[nav:current]"><a href="[nav:link]">[nav:name]</a></li>
{/fy:nav}
</header>
{fy:banners}
<img src="[banners:image]" alt="[banners:title]">
{/fy:banners}
</body>
</html>两个要点:
{fy:seohead}输出 canonical/OG/JSON-LD 等结构化标签,必须放在<head>内[nav:current]当前页时输出active,用于导航高亮
SEO 头部建议
每页至少输出:
{fy:seohead}(含 canonical/OG/JSON-LD){fy:pagekeywords}{fy:pagedescription}
详情页会自动生成 Article / Product / FAQPage 结构化数据。
性能建议
- 静态优先模式(后台「静态化设置」开启)下,页面直出 HTML,天然满足 LCP < 1.5s
- 图片使用
loading="lazy"并显式设置宽高(防 CLS) - CSS/JS 内联或本地化,避免阻塞渲染的外部请求
后台在线编辑
「设置 → 主题管理」可直接编辑当前主题的模板源文件并即时编译预览,无需访问服务器文件系统。