开发文档

鲜花交易系统与鲜花交易平台(PHP / ThinkPHP 版)的研发记录:功能上线、架构演进与实施经验,按发布日期持续更新。

自研模板标签引擎:让静态站点的列表页"活"起来

三条路,为什么选第三条

做企业官网或内容站,模板方案大致三种:

  1. 纯静态 HTML:最简单,但每发一篇文章都要手改页面,客户无法自助;
  2. 前端框架渲染(Vue/React):灵活,但私有化部署要多一个 Node 构建与路由层,且不利于 SEO;
  3. 服务端模板 + 静态生成:模板是 HTML 文件,服务端把数据填进去生成静态页,SEO 友好、部署只要 Apache/Nginx。

第三条最贴合"卖给客户、客户自己维护"的场景。

标签设计

我们实现了一套精简的标签,语法上借鉴 DedeCMS(很多国内建站客户熟悉这套写法),只保留够用的部分:

标签作用
{weilan:global.KEY/}站点级配置(模板目录、站点名、电话等)
{weilan:include file="header.html"/}引入公共片段,相对当前模板目录
{weilan:typelink code="news"/}输出栏目链接(也可按业务号)
{weilan:channellist tree="1"}…{/weilan:channellist}栏目循环,tree=1 取树、son=1 取子栏目
{weilan:arclist typeid="news" row="10"}…{/weilan:arclist}文章循环,支持置顶、推荐、条数、偏移、排序、每页条数
{weilan:pagelist/}分页条
[field:title/]、[field:pubdate format='m-d'/]、[field:info len=90/]循环内字段输出,支持截断与日期格式

设计上的两个取舍:

  • 不实现条件判断与复杂表达式。模板里一旦出现 if/else,客户就会写出无法维护的模板。需要分支就拆成两个模板。
  • 保留旧变量替换。自研模板标签引擎:让静态站点的列表页"活"起来、鲜花交易平台,CMS模板,静态化,ThinkPHP,Vue,鲜花交易系统 这类变量继续支持,让早期模板不改也能跑。

处理顺序决定成败

标签解析必须按固定顺序,否则会解析到一半的字符串:

  1. 全局配置(最外层,无依赖)
  2. include(把片段展开,后续标签才能生效)
  3. 栏目链接(单值替换)
  4. 上下文预替换(把 typeid=[field:code/] 落成当前栏目目录名)
  5. 栏目循环(内部若含 arclist,先内层渲染)
  6. 文章循环
  7. 分页
  8. 当前栏目/当前文章上下文字段
  9. 旧变量替换

第 4、5 步是最容易写错的:如果先跑 arclist 再跑 channellist,内层文章循环拿不到栏目上下文,模板就"通用化"失败——一个模板要服务所有栏目时,这个顺序必须正确。

一个真实缺陷:URL 口径必须与落盘一致

模板生成的链接和静态文件的实际路径,是两处独立代码算出来的。我们踩过一次:

  • 落盘路径用栏目的物化路径(/product/product-dev-docs/);
  • 文章链接却用栏目目录名(/product-dev-docs/)。

父级栏目因为"目录名与路径恰好相同"没暴露问题,子栏目一上线,所有文章链接都少一层父目录,点开全 404。

修复方式是把两处的口径统一到物化路径,并加了一条自检:生成后拿链接反推落盘路径,检查文件是否存在。这类"两处独立计算同一件事"的设计,一定要有交叉校验,否则错误只在特定层级出现。

分页与多页生成

arclist 带 pagesize 时,生成器按条数切页:第 1 页用规则的 url_pattern,第 n 页在扩展名前插 _p{n}(index.html → index_p2.html)。{weilan:pagelist/} 由生成器注入页码集合,避免模板自己去猜总页数。

小结

自研标签引擎的成本不高(核心是一个正则驱动的多趟替换),收益是模板可以被非开发人员修改:客户想加一个"相关阅读"区块,只需要在模板里写一段 arclist,不用等我们发版。

本文属于「开发文档」系列,记录鲜花交易系统(PHP + ThinkPHP 8 + MySQL 8)的实际研发进展。想了解功能清单与报价,可前往系统报价或联系我们。