ThinkPHP 6+ route/目录下PHP文件加载顺序固定:先app.php,再route.php,最后其他文件;app.php为主入口,应放高优先级规则,自定义文件需显式include_once引入,否则可能不加载。

route/目录下多个PHP文件的加载顺序
ThinkPHP 6+ 自动扫描 route/ 目录下的所有 PHP 文件,但**不按字母或时间排序**,而是按框架内部约定的固定优先级:先加载 app.php,再加载 route.php,最后才是其他命名的文件(如 api.php、admin.php)——这个顺序写死在路由加载器里,无法通过文件名控制。
常见错误是把通用规则写在 api.php,又在 app.php 里写了更宽泛的通配路由(比如 '<.>'</.>),结果请求直接被 app.php 拦截,api.php 的规则根本没机会匹配。
-
app.php是主路由入口,应放兜底、全局、高优先级规则 -
route.php是历史兼容入口,建议只保留它或统一用app.php,避免混淆 - 自定义文件(如
admin.php)需显式在app.php中引入:include_once __DIR__ . '/admin.php';,否则不一定被加载(尤其在 CLI 或某些部署环境下)
路由文件 vs 注解路由的优先级
注解路由(@route)不是“额外加载”,而是在控制器类被反射解析时动态注册的,它的执行时机晚于所有静态路由文件,因此**注解路由默认优先级最低**。
这意味着:如果你在 app.php 里写了 Route::get('user/:id', 'User/read'),又在 User 控制器上加了 @route("user/info"),两者不冲突;但若都匹配 /user/123,静态规则会先命中,注解规则不会生效。
立即学习“PHP免费学习笔记(深入)”;
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 注解路由只对当前控制器类生效,不能跨类覆盖
- 想让注解路由“抢”过静态路由?只能删掉对应路径的静态规则,没有开关可调优先级
- 调试时可用
php think route:list查看最终合并后的全部路由表,注解路由会带[annotation]标记
多应用模式下各应用路由是否隔离
是的,完全隔离。每个应用(如 app/admin/、app/api/)都有独立的 route/ 目录,各自加载自己的 app.php,互不影响。
但注意一个关键陷阱:如果你在根应用(app/)的 route/app.php 里写了 Route::domain('api.example.com', [...]),而 app/api/route/app.php 也定义了同域名路由,**实际生效的是根应用的定义**——因为域名绑定发生在应用启动前,由主应用路由系统接管,子应用路由只处理路径部分。
- 域名级路由必须在主应用(
app/)的路由文件中定义 - 子应用的路由只响应已进入该应用后的路径,例如
api.example.com/v1/user中的/v1/user - 别在子应用里重复写
Route::domain(),框架会忽略
为什么改了路由文件却没生效
最常见原因是缓存没清。ThinkPHP 默认开启路由缓存,修改 route/ 下任何 PHP 文件后,必须手动清除:php think route:clear。否则框架仍读取 runtime/route.php 缓存文件。
另一个隐蔽原因:你在 config/route.php 里设置了 'rules' => [] 或 'default_route_pattern' 等覆盖项,这些配置会干预路由解析逻辑,导致文件加载看似正常,但匹配行为异常。
- 检查
runtime/route.php是否存在且内容与你修改的文件一致 - 临时关闭路由缓存:在
config/app.php加'route_cache' => false,开发阶段更直观 - CLI 请求(如
php think命令)走的是命令行应用环境,其路由加载链与 HTTP 请求不同,不要用 CLI 测试 Web 路由
php think route:list 看最终结果,而不是靠文件名或直觉判断。


















