ThinkPHP6 资源发布到 CDN 后,模板继承里的 `__CDN__` 别名在子主题里"断档"了
上周给博客做了一次"体面升级":图片和 JS 全扔阿里云 OSS,再配上 CDN 域名。本地测试顺风顺水,`config/view.php` 里加了个 `__CDN__` 别名指向 `https://cdn.example.com/static/`,模板里 `__CDN__/js/app.js` 一写,编译出来整整齐齐。
问题出在我用了模板继承做主题系统。主主题 `base.html` 里声明了公共头尾,子主题 `home/index.html` 继承它,按说子主题只管填 `block` 就行。但上线后用户反馈首页样式全崩,F12 一看,子主题里引用的资源还是 `__CDN__` 开头,但主主题里的 `logo.svg` 和 `main.css` 居然回到了相对路径 `/static/...`,CDN 完全没沾边。
排查了两个小时,发现是个"编译时机"的坑。ThinkPHP 的模板编译会把 `__CDN__` 这种别名替换成真实路径,但编译缓存是按模板文件独立存的。我的子主题和主主题不在同一套目录结构里:子主题是运行时从 `extend/theme/home/` 加载的,主主题在 `view/base/`。更关键的是,主主题被编译时,`__CDN__` 别名还没生效——因为那个别名是我后来在子主题的 `View::config()` 里动态注入的,主主题的缓存早就生成完了。
说白了,模板继承是"拼积木",但每块积木的编译是各管各的。你给子主题喂的配置,管不到主主题已经编译好的缓存文件。
现在的解法有点糙但管用:把 `__CDN__` 直接写进 `config/view.php` 的 `tpl_replace_string`,让它在应用启动时就全局生效,而不是靠某个控制器的 `View::config()` 事后补刀。另外把主主题的编译缓存清了,强制重新编译一次。如果以后要做多 CDN 切换,估计得自己写个模板标签,让替换发生在运行时而不是编译期。
还有个附带发现:静态资源的"发布路径"和模板的"查找路径"在 ThinkPHP 里是两本账。`php think publish` 能把资源推到 public,但 CDN 化之后,你得自己保证模板引用的路径和实际发布的路径对得上。我一度以为 `__STATIC__` 会自动跟着 publish 走,结果它只是个字符串替换,跟文件到底在不在那儿毫无关系。
现在我的主题系统里多了条约定:任何新增别名必须在 `AppService.php` 的 `boot` 里注册,禁止在控制器里临时改。模板继承省了我重复写 HTML 的功夫,但省不了理解编译机制的时间。