模板继承写到第七层时,我终于承认自己对 `block` 和 `extend` 的理解还停留在"复制粘贴"阶段
上周给老项目做前端重构,手一滑把基础模板拆成了七层继承:layout → base → page → section → widget → form → input。当时还自我感动,觉得这叫"原子化设计"。结果上线后 CDN 上的 CSS 全炸了,浏览器控制台红得跟过年似的。
问题出在第四层。`section` 里我写了 `{block name="css"}{__block__}{/block}`,想着子模板追加就行。但 `widget` 层有个二货同事(三个月前的我自己)把 `{__block__}` 写成了 `{$__block__}`,TP 的模板引擎没报错,直接当空变量输出了。更绝的是 `form` 层又套了一层 `append`,到 `input` 层时,静态资源路径已经经历了三次"相对转绝对"的洗礼,最终产出了一堆类似 `//cdn.example.com/static/css/../../static/css/form/../../static/css/input.css` 的鬼东西。
排查过程很魔幻。本地开发环境用的 `php think run`,根目录就是 public,所有 `__STATIC__` 解析出来都是 `/static/...`,七层继承照样跑。一上预发环境,项目扔在子目录 `/v2/` 下面,问题全暴露了。`__STATIC__` 在第三层被某个 `include` 里的硬编码 `./static/` 截胡,后面四层全跟着跑偏。我盯着 Network 面板看了二十分钟,才发现有张背景图请求的是 `https://cdn.example.com/v2/static/css/static/img/static/...`,路径里嵌套了四层 static,俄罗斯套娃都没这么离谱。
后来逼着自己把资源加载拆成两条线:模板继承只负责 HTML 骨架,CSS/JS 全走资源清单文件。具体做法是在 base 层放一个 `{block name="res"}{/block}`,子模板不直接写 ``,而是传一个数组变量 `$res.css = ['form', 'input']`。最后由统一的服务端脚本读 JSON 映射表,输出带 hash 的 CDN 路径。这样模板层不管继承多深,资源路径只在最后一锤子买卖里决定,中间层想作妖都没机会。
还有个坑是 `__STATIC__` 和 `__CDN__` 混用。之前图省事,图片走 `__CDN__`,字体走 `__STATIC__`,结果某次运维切了 CDN 域名,字体全跨域报错。现在全站资源统一走一个入口,本地开发时映射到 webpack-dev-server,生产环境走 CDN,路径问题在构建阶段就解决掉,模板里只留占位符。
说个血泪教训:模板继承超过三层,一定要在每层开头写注释 ``,不然三个月后回来看,你根本分不清哪层在覆盖哪层的 block。我现在甚至想写个 IDE 插件,继承链可视化,鼠标悬停显示当前 block 的完整溯源路径。有没有老哥做过类似工具?求推荐。