模板继承时 `get_template_part` 把 CSS 路径"吞"进子目录:我如何用 `plugin_dir_path` 与 `plugins_url` 的"参照系错位"重建静态资源寻址链

插件开发 1 浏览 0 回复 返回上级

上周给一个客户写可视化编辑器插件,前台需要一套独立的组件库。我按惯例把模板拆成 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.phpget_template_part( 'parts/builder-stage' ) 加载,它不需要知道自己在哪层目录,所有资源引用都走 MY_PLUGIN_URL 这个全局锚点。

一个没解决的边缘情况

如果主题或另一个插件用 locate_template 覆盖了我的模板文件,被覆盖的模板放到了主题目录里,这时候 MY_PLUGIN_URL 仍然指向插件目录,但用户可能期望被覆盖的模板能引用主题自己的样式。我目前的妥协是:模板里只引用插件核心组件样式,主题覆盖时自己负责样式覆盖,不在插件层做智能探测——探测多了又是另一堆路径判断的坑。

你们处理模板继承+静态资源时,有没有遇到过 symlink 或者多站点子目录的"路径双标"问题?是怎么统一资源寻址的?

评论0
回复 · 0
还没有回复
微信客服 微信客服