EP220. “迁移 Slide 与 Slideshow:复制近似 Block 起步”
🔒 登录后可标记已读延续同一套迁移套路,把 Slide、Slideshow 两个 Block 也搬到新的 block.json 标准做法——这一讲进一步验证了一个高效技巧:不是每次都从最简单的占位 Block 复制,而是复制「结构最相似的那个已迁移 Block」(Slide 复制自刚迁移完的 Banner,因为两者都有背景图选择器;Slideshow 复制自 Slide,因为两者都是简单的 InnerBlocks 容器),能省下大量重复劳动。这一讲也再次印证「先跑起来看报错」的调试节奏——反复出现「忘记 import useBlockProps」这个同一类小错误,靠浏览器控制台报错信息迅速定位修复,比试图一次写对更省心。
涉及文件
wp-content/themes/fictional-clean-blocks/functions.php(修改,slide/slideshow从旧JSXBlock切到register_block_type_from_metadata)wp-content/themes/fictional-clean-blocks/src/slide/整套(新建,复制自banner/)wp-content/themes/fictional-clean-blocks/src/slideshow/整套(新建,复制自slide/)- 删除旧的
our-blocks/slide.js/slide.php、slideshow.js/slideshow.php
代码实现
src/slide/block.json(复制自 banner/block.json 再改,新增 themeimage 属性):
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "ourblocktheme/slide",
"title": "Fictional University Slide",
"supports": {
"align": ["full"]
},
"attributes": {
"themeimage": { "type": "string" },
"align": { "type": "string", "default": "full" },
"imgID": { "type": "number" },
"imgURL": { "type": "string" }
},
"editorScript": "file:./index.js",
"render": "file:./render.php"
}
src/slide/edit.js(完整文件,从旧的 our-blocks/slide.js 搬运,两处默认图回退逻辑都改用 ourThemeData.themePath):
import apiFetch from "@wordpress/api-fetch"
import { Button, PanelBody, PanelRow } from "@wordpress/components"
import {
useBlockProps,
InnerBlocks,
InspectorControls,
MediaUpload,
MediaUploadCheck
} from "@wordpress/block-editor"
import { useEffect } from "@wordpress/element"
export default function Edit(props) {
const blockProps = useBlockProps()
useEffect(function () {
if (props.attributes.themeimage) {
props.setAttributes({
imgURL: `${ourThemeData.themePath}/images/${props.attributes.themeimage}`
})
}
if (!props.attributes.themeimage && !props.attributes.imgURL) {
props.setAttributes({ imgURL: `${ourThemeData.themePath}/images/library-hero.jpg` })
}
}, [])
useEffect(
function () {
if (props.attributes.imgID) {
async function go() {
const response = await apiFetch({
path: `/wp/v2/media/${props.attributes.imgID}`,
method: "GET"
})
props.setAttributes({
themeimage: "",
imgURL: response.media_details.sizes.pageBanner.source_url
})
}
go()
}
},
[props.attributes.imgID]
)
function onFileSelect(x) {
props.setAttributes({ imgID: x.id })
}
return (
<>
<InspectorControls>
<PanelBody title="Background" initialOpen={true}>
<PanelRow>
<MediaUploadCheck>
<MediaUpload
onSelect={onFileSelect}
value={props.attributes.imgID}
render={({ open }) => {
return <Button onClick={open}>Choose Image</Button>
}}
/>
</MediaUploadCheck>
</PanelRow>
</PanelBody>
</InspectorControls>
<div {...blockProps}>
<div
className="hero-slider__slide"
style={{ backgroundImage: `url('${props.attributes.imgURL}')` }}
>
<div className="hero-slider__interior container">
<div className="hero-slider__overlay t-center">
<InnerBlocks
allowedBlocks={["ourblocktheme/genericheading", "ourblocktheme/genericbutton"]}
/>
</div>
</div>
</div>
</div>
</>
)
}
src/slideshow/edit.js(完整文件,比 Slide 简单得多,只是一层 InnerBlocks 容器):
import { InnerBlocks, useBlockProps } from "@wordpress/block-editor"
export default function Edit(props) {
const blockProps = useBlockProps()
return (
<div {...blockProps}>
<div style={{ backgroundColor: "#333", padding: "35px" }}>
<p style={{ textAlign: "center", fontSize: "20px", color: "#FFF" }}>Slideshow</p>
<InnerBlocks allowedBlocks={["ourblocktheme/slide"]} />
</div>
</div>
)
}
关键改动点:
- 迁移顺序按「结构相似度」选复制源,不是每次都从头搭:Slide 几乎和 Banner 一模一样(都要背景图选择器 + 只允许嵌套标题/按钮),直接复制
banner/整个文件夹改名字比重新搭快得多;Slideshow 内容比 Slide 简单很多(没有背景图,只是个容器),但复制slide/依然比复制banner/省事——只需要删掉不需要的部分,而不是从零补充 block.json的attributes要跟着实际需求增减:Slide 比 Banner 多了themeimage属性(模板文件里给幻灯片指定默认示意图的机制,Block Theme 章节 EP205-207 学过);Slideshow 则完全不需要imgID/imgURL/themeimage这几个跟背景图相关的属性,block.json里直接删掉这些不需要的字段themeimage相关的两层回退逻辑,写法上跟旧版本本质一致,只是数据来源变了:原本旧版本靠JSXBlock构造函数第三参数注入的slide.themeimagepath,现在统一用 EP219 建立的全局变量ourThemeData.themePath——两层判断逻辑(有themeimage就拼主题目录里的对应文件;themeimage和imgURL都没有就退回通用的library-hero.jpg)完全照搬,只改了变量名- 多次遇到「忘记导入
useBlockProps」的报错:apiVersion: 3强制要求手动接管选中态外壳,这个 Hook 但凡漏导入就会在控制台报「useBlockProps未定义」——这一讲连续在 Slide 和 Slideshow 上都踩到同样的错,每次都是靠「刷新页面 → 打开控制台 → 看报错信息 → 找到漏导入的那一行」这套流程快速定位,印证了「不追求一次写对,靠报错信息迭代」是更实际的工作节奏 - 需要额外补一层
<div {...blockProps}>包装外壳时的位置技巧:如果原本借用的旧代码本身已经有一个「看起来适合当外壳」的最外层<div>,可以直接在这个<div>上加{...blockProps};但像 Slide 这种最外层其实是InspectorControls(不适合当选中态外壳的元素)打头的场景,需要额外单独包一层新的<div {...blockProps}>才行,不能图省事直接套在语义不合适的元素上 save函数完全不用改动:只要 Block 本身是靠InnerBlocks.Content保存内容(Slide/Slideshow 都是),复制过来的index.js里这部分代码原封不动就能用,不需要额外调整- 验证嵌套功能仍然正常:迁移完成后重新测试「往 Slideshow 里插入第 4 张 Slide、往 Slide 里插入标题和按钮」,确认整条嵌套链路(Slideshow → Slide → 标题/按钮)没有因为迁移而损坏
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
| (复用 EP217-219 已介绍过的工具,本讲无新增) | — | — |
常见坑
- 从零开始逐行重写相似的 Block,而不是先判断「有没有更接近的已迁移 Block 可以复制」——浪费大量重复劳动
- 忘记在新文件里导入
useBlockProps——apiVersion: 3下漏了这个 Hook 会导致编辑器直接报错,且很容易在复制粘贴时被忽略 - 把
{...blockProps}加在语义不合适的中间层元素上(比如InspectorControls或者应该保持纯样式作用的内层div),而不是新建一个专门的最外层包装<div> - 迁移完 Block 后忘记同时验证「嵌套」链路(Slideshow 里插 Slide、Slide 里插标题/按钮)依然正常工作,只测试了单个 Block 本身
延伸 / 后续讲座会用到
下一讲要迁移标题(genericheading)和按钮(genericbutton),这两个不需要 render.php 文件,作者预告会「更快更简单」。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 29, EP220