三条路,为什么选第三条
做企业官网或内容站,模板方案大致三种:
- 纯静态 HTML:最简单,但每发一篇文章都要手改页面,客户无法自助;
- 前端框架渲染(Vue/React):灵活,但私有化部署要多一个 Node 构建与路由层,且不利于 SEO;
- 服务端模板 + 静态生成:模板是 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,鲜花交易系统这类变量继续支持,让早期模板不改也能跑。
处理顺序决定成败
标签解析必须按固定顺序,否则会解析到一半的字符串:
- 全局配置(最外层,无依赖)
- include(把片段展开,后续标签才能生效)
- 栏目链接(单值替换)
- 上下文预替换(把
typeid=[field:code/]落成当前栏目目录名) - 栏目循环(内部若含 arclist,先内层渲染)
- 文章循环
- 分页
- 当前栏目/当前文章上下文字段
- 旧变量替换
第 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,不用等我们发版。