EP198. “wp_localize_script 把 PHP 数据传给编辑器 JS”
🔒 登录后可标记已读📌 并入 EP197 的提醒:跟 EP195 内容相同的重复提醒——our-blocks/banner.php 里 if (!$attributes['imgURL']) 建议改成 if (!isset($attributes['imgURL'])),避免较新 PHP 环境下的「未定义数组键」警告。
收尾 Banner Block 的几个小细节:① 编辑器里刚插入一个全新的 Banner 实例时,背景图是空的(不像前台有 PHP 兜底的默认图)——用 wp_localize_script() 把 PHP 端知道的「默认背景图路径」注入成一段 JS 能直接读到的全局变量,让编辑器和前台使用同一个默认值来源,不用在 JS 里硬编码、也不用假设网站装在根目录;② 给 useEffect 里发起图片查询的逻辑加一层判断,避免用户还没选任何图片时就白白发一次「查不存在的媒体 ID」的请求;③ 一个纯 CSS 收尾(背景图水平居中)。做完这一讲,Banner Block 正式完工。
涉及文件
wp-content/themes/fictional-university-block-theme/functions.php(修改,JSXBlock类新增第三参数$data)wp-content/themes/fictional-university-block-theme/our-blocks/banner.js(修改)wp-content/themes/fictional-university-block-theme/css/modules/page-banner.scss(修改,纯 CSS,不展开)
代码实现
functions.php:JSXBlock 类新增可选的第三参数 $data,为真时用 wp_localize_script() 注入:
class JSXBlock {
function __construct($name, $renderCallback = null, $data = null) {
$this->name = $name;
$this->data = $data;
$this->renderCallback = $renderCallback;
add_action('init', [$this, 'onInit']);
}
function onInit() {
wp_register_script($this->name, get_stylesheet_directory_uri() . "/build/{$this->name}.js", array('wp-blocks', 'wp-editor'));
if ($this->data) {
wp_localize_script($this->name, $this->name, $this->data);
}
$ourArgs = array(
'editor_script' => $this->name
);
if ($this->renderCallback) {
$ourArgs['render_callback'] = [$this, 'ourRenderCallback'];
}
register_block_type("ourblocktheme/{$this->name}", $ourArgs);
}
}
new JSXBlock('banner', true, ['fallbackimage' => get_theme_file_uri('/images/library-hero.jpg')]);
new JSXBlock('genericheading');
new JSXBlock('genericbutton');
our-blocks/banner.js:imgURL 默认值直接读取 PHP 注入的全局变量;useEffect 里加判断避免无意义请求:
attributes: {
align: { type: "string", default: "full" },
imgID: { type: "number" },
imgURL: { type: "string", default: banner.fallbackimage }
},
function EditComponent(props) {
useEffect(
function () {
if (props.attributes.imgID) {
async function go() {
const response = await apiFetch({
path: `/wp/v2/media/${props.attributes.imgID}`,
method: "GET"
})
props.setAttributes({ imgURL: response.media_details.sizes.pageBanner.source_url })
}
go()
}
},
[props.attributes.imgID]
)
// ...其余不变...
}
关键改动点:
- 问题背景:
imgURL之前没有默认值,前台靠banner.php里的 PHP 兜底逻辑(EP194-196)显示默认图;但编辑器里新插入的 Banner 实例,imgURL就是空字符串,看起来像「背景图丢失」——想让编辑器也有默认图,但又不想在 JS 里写死一个假设了固定安装路径的字符串(万一 WordPress 装在子目录、或者wp-content被改名,写死路径就会失效) wp_localize_script($handle, $对象名, $数据数组)——WordPress 提供的「把 PHP 数据传给已注册 JS 脚本」的标准工具:调用后,WordPress 会在页面里,紧挨着这个脚本的<script>标签之前,输出一段用传入数据生成的 JS 变量声明——效果上就是把 PHP 算好的值,变成前端 JS 里可以直接读到的全局变量JSXBlock类新增可选的第三参数$data = null:默认null,只有真正需要往 JS 注入数据的 Block(这里是banner)才会传第三个参数;在onInit()里加一层if ($this->data) { wp_localize_script(...) }判断,为空时完全不调用wp_localize_script()的三个参数分别用了同一个$this->name:第一个参数是脚本 handle(要挂靠在哪个已注册脚本上),第二个参数是要生成的 JS 变量名——这里两个都统一用 Block 名字banner(因为JSXBlock类本身就是拿$name当脚本 handle 用的),第三个参数才是真正的数据数组new JSXBlock('banner', true, ['fallbackimage' => get_theme_file_uri('/images/library-hero.jpg')]):第三个参数是一个关联数组,只有一个键fallbackimage(全部小写连写),值是 PHP 动态算出来的默认图片完整 URL——这一步把「怎么找到当前主题目录」这件在 JS 里做不到(或者很麻烦)的事,交给 PHP 的get_theme_file_uri()完成- JS 端直接读取这个被注入的全局变量:
imgURL: {type: "string", default: banner.fallbackimage}——banner在浏览器里就是一个全局对象(由wp_localize_script()生成),.fallbackimage对应 PHP 数组里那个键;效果是新插入的 Banner 实例默认就带着跟 PHP 端完全一致的默认图 URL,不需要重复维护两份写死的路径 - 给
useEffect里的图片查询逻辑加上if (props.attributes.imgID)判断:只有用户真的选过图片(imgID有值)才去发请求查询对应的pageBanner尺寸 URL——避免「Block 刚插入、还没选任何自定义图片」时,就对着一个不存在的媒体 ID 发一次注定失败的请求,污染控制台报错信息 - 纯 CSS 收尾:给
page-banner__bg-image补上background-position: center center;,确保背景图水平方向也居中显示(之前可能因为图片本身构图问题,靠左或靠右显示不够居中)——这一步跟 WordPress/JS 都没关系,纯粹是视觉细节
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
wp_localize_script($handle, $对象名, $数据数组) | WP 内建 function | 把 PHP 端的数据注入成 JS 全局变量,供已注册的脚本直接读取 |
常见坑
- 在 JS 里写死一个假设安装路径的默认图字符串——一旦网站装在子目录或改了
wp-content文件夹名,路径就会失效;应该让 PHP 算好再通过wp_localize_script()传给 JS useEffect里的图片查询逻辑没有判断imgID是否存在——每次插入全新的 Block 实例都会对不存在的媒体 ID 发一次必然失败的请求JSXBlock类的新参数$data没有默认值null——所有不需要注入数据的已有 Block(genericheading/genericbutton)实例化时都要被迫多传一个参数
延伸 / 后续讲座会用到
Banner Block 正式完工,下一讲开始做首页「近期活动 + 最新博文」这个区域。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 28, EP197, EP198