模板继承时 `get_template_part` 把 CSS 路径"吞"进子目录:我如何用 `plugin_dir_path` 与 `plugins_url` 的"参照系错位"重建静态资源寻址链
上周给一个客户写可视化编辑器插件,前台需要一套独立的组件库。我按惯例把模板拆成 templates/ 目录,静态资源丢 assets/,结果本地开发一切正常,打包给客户部署后,模板能加载,CSS 和 JS 全 404。
问题的根子不在 URL 拼错,而是我对 WordPress 两套路径函数的理解一直停留在"能跑就行"的层面。
第一坑:plugin_dir_path 返回的是"文件系统视角"
我的目录结构长这样:
my-plugin/
├── my-plugin.php
├── assets/
│ ├── css/
│ │ └── builder.css
│ └── js/
│ └── builder.js
└── templates/
├── builder-wrapper.php
└── parts/
└── builder-stage.php
在 builder-wrapper.php 里,我一开始这么写:
<link rel="stylesheet" href="<?php echo plugin_dir_path( __FILE__ ) . '../assets/css/builder.css'; ?>">
本地 Windows 环境居然能跑,因为文件系统路径里 ../ 被解析了。但上线后 Linux 服务器 + Nginx,浏览器收到的 href 是 /var/www/html/wp-content/plugins/my-plugin/templates/../assets/css/builder.css,直接 404。更隐蔽的是,有些环境下 plugin_dir_path 末尾带斜杠,有些不带,拼接时还得先 trailingslashit 再处理,否则会出现双斜杠。
第二坑:plugins_url 的"锚点漂移"
改成 plugins_url 后,我在 builder-wrapper.php 里写:
<link rel="stylesheet" href="<?php echo plugins_url( '../assets/css/builder.css', __FILE__ ); ?>">
这版在单站点正常了,但客户是多站点子目录模式(example.com/site2/),plugins_url 返回的 URL 没带子目录前缀,资源又 404。查了下源码才发现,plugins_url 的第二个参数 $plugin 是用来计算相对路径的参照文件,但如果插件是通过 symlink 部署的(客户用 Bedrock 结构),__FILE__ 解析出来的是真实路径,而 WordPress 的 WP_PLUGIN_URL 是 Web 可访问路径,两者不在同一个"命名空间"里,dirname 切割时会把 symlink 的层级算进去,导致相对路径多跳或少跳一层。
我的解法:在插件主文件里"冻结"基准 URL
不再让模板文件自己算路径,而是在插件初始化时定义两个常量,把"文件系统根"和"Web 可访问根"一次性钉死:
// my-plugin.php
if ( ! defined( 'MY_PLUGIN_DIR' ) ) {
define( 'MY_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
}
if ( ! defined( 'MY_PLUGIN_URL' ) ) {
define( 'MY_PLUGIN_URL', plugins_url( '/', __FILE__ ) );
}
注意第二个参数传 __FILE__ 且路径部分给 '/',这样返回的是插件目录的 URL(带末尾斜杠),不受具体调用文件位置影响。然后模板里统一用:
<link rel="stylesheet" href="<?php echo esc_url( MY_PLUGIN_URL . 'assets/css/builder.css' ); ?>">
模板继承时,子模板 builder-stage.php 被 get_template_part( 'parts/builder-stage' ) 加载,它不需要知道自己在哪层目录,所有资源引用都走 MY_PLUGIN_URL 这个全局锚点。
一个没解决的边缘情况
如果主题或另一个插件用 locate_template 覆盖了我的模板文件,被覆盖的模板放到了主题目录里,这时候 MY_PLUGIN_URL 仍然指向插件目录,但用户可能期望被覆盖的模板能引用主题自己的样式。我目前的妥协是:模板里只引用插件核心组件样式,主题覆盖时自己负责样式覆盖,不在插件层做智能探测——探测多了又是另一堆路径判断的坑。
你们处理模板继承+静态资源时,有没有遇到过 symlink 或者多站点子目录的"路径双标"问题?是怎么统一资源寻址的?