Vite构建后JS/CSS 404的根本原因是版本号未对齐、路径未映射及模板仍用__STATIC__硬拼路径;需开启build.manifest、统一用static_url()函数读manifest.json动态替换,并彻底清理模板中所有__STATIC__和url('@static/')残留。

前端打包后资源加载慢,根本不是 CDN 没配好,而是版本号没对上、路径没映射、模板还在用 __STATIC__ 硬拼路径——Vite 输出的 app.a1b2c3d4.js,你模板里写的是 app.js,不 404 才怪。
为什么 Vite 构建后 JS/CSS 总是 404?
ThinkPHP 默认不识别 Vite 的哈希文件名,asset() 或 url('@static/js/app.js') 还在找原名,而实际文件已变成 app.a1b2c3d4.js。更麻烦的是,Vite 的 manifest.json 路径默认在 public/.vite/manifest.json,但 ThinkPHP 的 build.php 或自定义函数若没读这个文件,就完全无法映射。
- Vite 构建必须开启
build.manifest: true,否则没有manifest.json - ThinkPHP 模板中引用路径(如
js/app.js)必须和manifest.json中的 key 完全一致:大小写、斜杠方向、是否带前缀都不能错 -
url('@static/js/app.js')是错的——@static是 ThinkPHP 自己的别名机制,和 Vite 无关;应统一用自定义函数,比如static_url('js/app.js') - 别把 Vite 的
public目录直接当 ThinkPHP 的public/static——构建后需把dist/内容合并拷贝到public/static,且确保manifest.json也在该目录下可被 PHP 读取
如何让 static_url() 正确读 manifest 并拼 CDN + hash?
不能靠时间戳 ?v=<?php echo time() ?>,它让 CDN 缓存失效;也不能硬写 ?v=a1b2c3d4,每次构建都要手动改模板。正确做法是运行时读 manifest.json,动态注入 hash。
- 在
app/common.php或视图初始化处定义static_url($path)函数 - 函数内先检查
file_exists(public_path() . '/static/.vite/manifest.json'),再json_decode(file_get_contents(...), true) - 若
$manifest[$path]存在,返回config('app.cdn_domain') . '/static/' . $manifest[$path];否则回退到/static/$path -
config('app.cdn_domain')来自config/app.php,值为env('CDN_DOMAIN', ''),开发环境留空,生产填https://cdn.example.com - 模板中统一写
{:static_url('js/app.js')},不再出现{__STATIC__}或url('@static/...')
CDN 域名生效但资源还是走本地?检查这三处
CDN 配了,域名也拼对了,但浏览器 Network 里看到的仍是 https://yourdomain.com/static/js/app.a1b2c3d4.js,说明替换逻辑被绕过了。
立即学习“PHP免费学习笔记(深入)”;
- 确认没在模板里混用
{__STATIC__}/js/app.js——这个常量由tpl_replace_string控制,但它是字符串替换,不走manifest,也不支持 hash,必须全部删掉 - 检查
config/view.php中是否还保留'__STATIC__' => env('STATIC_DOMAIN', '/static'),如果留着,它会和static_url()冲突,优先级取决于渲染顺序,结果不可控 - 确认 Nginx/Apache 没把
/static/路径反向代理回 PHP ——CDN 流量必须绕过源站,静态资源路径要直出,否则 CDN 只是摆设
真正卡住人的不是 Vite 怎么打包,而是 ThinkPHP 模板里那一堆残留的 __STATIC__、url('@static/...') 和硬编码路径——只要有一处漏改,那个文件就永远走不了 CDN,也永远得不到 hash 版本更新。动手前,先全局搜索删干净再说。



















