- 前端
- CMS
【免费下载链接】wp-calypso
The JavaScript and API powered WordPress.com
@automattic/block-renderer是 wp-calypso(WordPress.com 的 JavaScript 与 API 前端)中的客户端区块渲染库,它让 React 应用能够在不进入 wp-admin 编辑器的情况下,拉取 WordPress 服务端渲染好的 Gutenberg 区块 HTML,并在独立 iframe 中按站点主题样式精确呈现。本文基于 packages/block-renderer/README.md 与其源码实现,系统讲解四个核心组件的用法、底层数据流、iframe 缩放与资源加载机制,并结合 wp-calypso 的 Pattern Library(图案库)真实消费场景给出可落地的集成方案。读完本文,你将能够独立使用该库搭建区块/图案预览组件,并理解其性能优化与样式隔离策略。
一、为什么需要客户端区块渲染
在 wp-calypso 中,很多界面(如 Pattern Library、站点搭建流程)需要在常规 React 页面中展示 Gutenberg 区块的实际效果——包括主题样式、全局样式、排版与区块内置脚本。直接嵌入原始 HTML 无法还原这些效果,而引入完整 Gutenberg 编辑器又过于笨重。
@automattic/block-renderer的解决方案是:客户端(浏览器端)渲染——由 WordPress.com 服务端(wpcom/v2的block-renderer系列接口)预先渲染出区块/图案的 HTML、样式与脚本,客户端再把这些产物放进一个隔离的 iframe 中呈现。这样既保留了区块的真实观感,又让宿主页面与区块文档彼此隔离。
该库以@automattic/block-renderer为名发布,声明为 "Render blocks on the client side"(见 packages/block-renderer/package.json),是一个仅面向 Calypso 内部使用的 workspace 包("private": true),构建产物支持 ESM/CJS 与类型声明(main/module/types字段)。
二、快速上手:最小可用示例
README 给出了一个非常简洁的组合示例,其中包含了两个 Provider 与一个渲染组件,这也是该库推荐的标准嵌套结构:
import { BlockRendererProvider, PatternsRendererProvider, PatternRenderer, } from '@automattic/block-renderer'; const PatternsPreview = () => ( <BlockRendererProvider siteId={ siteId } stylesheet={ stylesheet }> <PatternsRendererProvider siteId={ siteId } stylesheet={ stylesheet } patternIds={ patternIds }> <PatternRenderer patternId={ patternId } /> </PatternsRendererProvider> </BlockRendererProvider> );三个角色分工明确:
BlockRendererProvider负责"设置"层:拉取站点区块渲染所需的全局设置并注入blockEditorStore;PatternsRendererProvider负责"数据"层:批量拉取指定图案的渲染产物(HTML/样式/脚本)并放入PatternsRendererContext;PatternRenderer负责"呈现"层:读取某个patternId的渲染产物,交给 iframe 展示。
值得注意的是,README 中的patternIds在真实源码中实际是按类别分组的对象patternIdsByCategory: Record< string, string[] >,详见下文第四节。
三、核心组件逐个拆解
3.1 BlockRendererProvider:初始化渲染设置
职责:初始化区块渲染器的设置。它会从一个端点拉取设置,并存储到blockEditorStore(Gutenberg 的编辑器数据 Store)中。
从 block-renderer-provider.tsx 的实现可以看到它真正的完整职责:
- 通过
useBlockRendererSettings( siteId, stylesheet, useInlineStyles )拉取设置(TanStack Query 数据); - 通过
useSafeGlobalStylesOutput()(来自@automattic/global-styles)获取当前站点的全局样式; - 把设置中的样式、全局样式、以及两条"预览专用"的内联 CSS 合并成最终
settings:body{height:auto;overflow:hidden;}—— 避免预览出现滚动条;body{padding:0;}—— 避免编辑器自带的页面内边距干扰预览;
- 在设置未就绪(
isReady === false)时渲染placeholder(默认为null),避免渲染半成品; - 整个组件外层用
withExperimentalBlockEditorProvider包装——这一步正是"存储到blockEditorStore"的实现方式,它来自@automattic/global-styles包。
其 Props 定义如下(源码 block-renderer-provider.tsx):
| Props | 类型 | 说明 |
|---|---|---|
siteId | number \| string | 目标站点 ID,用于请求该站点的渲染设置 |
stylesheet | string | 主题样式表标识(默认''),决定渲染所用的主题样式 |
children | JSX.Element | 需要渲染的区块/图案内容 |
useInlineStyles | boolean | 是否要求服务端返回内联样式(默认false) |
placeholder | JSX.Element \| null | 设置加载完成前的占位内容(默认null) |
3.2 BlockRendererContainer:iframe 容器与缩放
职责:渲染一个 iframe 来控制区块的样式作用域,其 children 可以是任意你想要渲染的区块。
实现细节(block-renderer-container.tsx):
- 组件灵感直接来自 Gutenberg 的
block-preview/auto.js(源码注释明确标注了出处); - 内部使用
@wordpress/block-editor的__unstableIframe(Iframe)与__unstableEditorStyles(EditorStyles),通过__dangerousOptInToUnstableAPIsOnlyForCoreModules解锁私有 API 获取getDuotoneFilter; - 等比缩放:外层通过
useResizeObserver监听容器宽度,按scale = containerWidth / viewportWidth缩放 iframe 内容,默认视口宽度viewportWidth = 1200(与 Pattern 库的GRID_VIEW_VIEWPORT_WIDTH = 1200一致,见 pattern-preview/index.tsx); - 高度自适应:监听 iframe 内容高度
contentHeight,缩放后得到宿主元素高度;scaledHeight = contentHeight * scale || minHeight,并支持通过maxHeight(默认取常量BLOCK_MAX_HEIGHT = 2000,见 constants.ts)限制最大高度,超出时以maxHeight * scale截断; - 避免未样式内容闪现(FOUC):iframes 在
isLoaded为false时opacity: 0,待样式与脚本都加载完成才显示; - 交互隔离:iframe 设置
aria-hidden、tabIndex={ -1 }、loading="lazy"、pointerEvents: 'none',保证预览只读、不抢焦点、不影响宿主页面交互; - Safari 兼容处理:源码针对 Safari 中
Iframe注入的body{ background: white }与主题背景色产生特异性冲突的问题,用正则把主题 CSS 中body规则内的background-color追加!important; - Duotone 滤镜:从
settings.__experimentalFeatures?.color?.duotone读取默认/主题预设,用getDuotoneFilter生成 SVG 滤镜并以dangerouslySetInnerHTML注入——注释特别说明这些滤镜必须渲染在 children 之前,以避免 Safari 渲染问题。
3.3 PatternsRendererProvider:批量加载图案渲染产物
职责:初始化传入的图案 ID 集合,拉取每个图案的渲染 HTML 并存入PatternsRendererContext。
源码中的真实形态(patterns-renderer-provider.tsx)与 README 示例略有出入:它接收的是patternIdsByCategory: Record< string, string[] >(按类别分组)、siteInfo(可选,含title/tagline,会被传给服务端参与渲染)以及shouldShufflePosts(是否打乱博客文章类图案中文章的顺序)。
README 特别强调:"Note that it fetches 20 patterns per request to avoid any potential performance issues."(每请求获取 20 个图案以避免潜在性能问题)。从 use-rendered-patterns.ts 的源码看,它使用@tanstack/react-query的useQueries按类别并行发起请求,每个类别的pattern_ids参数以逗号连接,combine回调把各类别返回的图案合并为一份RenderedPatterns映射(合并函数被定义在 Hook 外部并通过combine传入,以保证 memo 稳定、避免无效化所有 context 消费者)。
3.4 PatternRenderer:渲染单个图案
职责:按patternId渲染对应图案。
实现要点(pattern-renderer.tsx):
- 从
usePatternsRendererContext()取出renderedPatterns[ patternId ]; - 若传入
viewportHeight,先用normalizeMinHeight把图案 HTML 中的min-height: Nvh换算为像素(N * viewportHeight / 100px),保证图案在指定视口下占满高度,见 normalize-min-height.ts; - 支持
transformHtml?: ( patternHtml: string ) => string回调,对 HTML 做自定义加工后再渲染; - 合并样式:默认将
styles、pattern.styles合并;若shouldShufflePosts为true且图案是"博客文章网格",则通过shufflePosts生成一段内联 CSS,用order属性打乱网格中文章的顺序——这是为了让图案列表中多个"博客类"图案的封面图不至于看起来千篇一律(见 shuffle-posts.ts),并且会按patternId记忆化顺序、用全局lastOffset递增,保证同一图案每次预览顺序一致、不同图案顺序不同; - 合并脚本:
pattern.scripts与外部传入的scripts拼接后一起注入; - 将最终 HTML 以
dangerouslySetInnerHTML注入BlockRendererContainer,并用memo包裹避免无关重渲染。
四、数据流与 API 端点
整个库的数据链路可以概括为两条请求,均走wpcom-proxy-request、wpcom/v2API 命名空间:
1)拉取渲染设置(use-block-renderer-settings.ts):
GET /sites/{siteId}/block-renderer/settings| 查询参数 | 说明 |
|---|---|
stylesheet | 主题样式表标识 |
use_inline_styles | 是否内联样式(布尔字符串) |
查询键为[ siteId, 'block-renderer', stylesheet, useInlineStyles ],staleTime: Infinity(设置被视为恒定,避免重复请求),meta.persist: false。
2)批量渲染图案(use-rendered-patterns.ts):
GET /sites/{siteId}/block-renderer/patterns/render| 查询参数 | 说明 |
|---|---|
stylesheet | 主题样式表标识 |
category | 图案类别(请求按类别分组) |
pattern_ids | 逗号连接的图案 ID 列表(README 建议每请求约 20 个) |
_locale | 当前语言环境(取自useLocale()) |
site_title | 站点标题(可选,来自siteInfo) |
site_tagline | 站点副标题(可选,来自siteInfo) |
图案渲染请求设置staleTime: 0且refetchOnWindowFocus: false,说明渲染产物每次进入页面都会重新获取,但不因窗口聚焦而刷新。
返回的数据结构由 types.ts 定义:
export type RenderedStyle = { css: string; isGlobalStyles: boolean; __unstableType?: string; }; export type RenderedPattern = { ID: number; title: string; html: string; styles: RenderedStyle[]; scripts: string; }; export type RenderedPatterns = { [ key: string ]: RenderedPattern; }; export type SiteInfo = { title?: string; tagline?: string; };五、iframe 内的样式与脚本加载机制
为避免预览时出现"先闪未样式内容、再应用主题"的糟糕体验,BlockRendererContainer通过 use-parsed-assets.ts、load-styles.ts 与 load-scripts.ts 手工控制资源加载:
useParsedAssets把服务端返回的 HTML 字符串(如assets.styles、assets.scripts)解析进一个createHTMLDocument创建的临时文档中,再取出其中的子节点(<link>/<script>元素);loadStyles/loadScripts用 Promise 链串行把这些节点逐个克隆进 iframe 的<body>,并分别监听onload/onerror——出错时仅console.warn并照常 resolve,避免单个资源失败阻塞整个预览;- 全部资源加载完毕后,
contentAssetsRef的回调才会setIsLoaded( true ),让 iframe 由opacity: 0变为可见; - 同时组件还会注入
__unstableResolvedAssets中解析出的样式(styleAssets)与脚本,配合EditorStyles把合并后的RenderedStyle[]渲染进 iframe 头部,从而完整还原主题观感。
此外,iframe 内部文档的<html>与<body>会被设置绝对定位与 100% 宽度,这是contentResizeListener(useResizeObserver)能准确测量内容高度的前提。
六、仓库中的真实应用:Pattern Library 预览
@automattic/block-renderer在 wp-calypso 中的主要消费者是「我的站点 → 图案」(Patterns)模块,其入口在 client/my-sites/patterns/components/pattern-preview/index.tsx,同目录的pattern-gallery/client.tsx与category-gallery/client.tsx也引用了本库。
PatternPreview是理解各组件如何协同工作的最佳示例:
- 它通过
usePatternsRendererContext()读取renderedPatterns,用pattern?.ID编码后的patternId索引到渲染产物,据此给预览容器加is-loading状态类; - 向
PatternRenderer传入:maxHeight="none"(不限制高度,按图案实际内容展示);minHeight={ nodeSize.width / ASPECT_RATIO }(按 7:4 的宽高比保证占位高度,ASPECT_RATIO = 7 / 4);viewportWidth(固定视口时为GRID_VIEW_VIEWPORT_WIDTH = 1200,否则按容器宽度 ×1.16 动态计算);styles={ noClickStyles }:注入a[href], button, input, textarea { pointer-events: none; },防止用户从预览 iframe 中误点击跳走或提交表单;scripts={ redrawScript }:注入一段延迟重绘脚本,规避 Firefox/Safari 对writing-mode样式元素在 iframe 中渲染异常的问题;
- 外层还套了
ResizableBox支持拖拽调整预览宽度(RTL 场景调整左侧手柄),配合useResizeObserver触发patternPreviewResize自定义事件。
这套组合清晰展示了"宿主页面管布局与交互、BlockRendererContainer管样式隔离与缩放、PatternRenderer管数据注入"的分层设计。
七、性能与使用注意事项
综合 README 与源码,使用该库时有几点值得注意:
- 批量而非逐个请求:图案渲染产物必须按类别分组批量获取(每请求约 20 个图案),避免为每个图案单独发请求造成接口压力;这一约束直接决定了
PatternsRendererProvider需要patternIdsByCategory而非扁平数组; - 设置请求被强缓存:
useBlockRendererSettings使用staleTime: Infinity,同一siteId + stylesheet组合在会话内只请求一次,适合在页面顶部统一包一个BlockRendererProvider供多个预览共享; - 渲染产物不入持久缓存:
meta.persist: false表示设置不写入持久化状态,图案渲染结果同样每次重新拉取(staleTime: 0),以保证图案改动立即可见; - 预览 iframe 只读:默认
pointerEvents: 'none',如需可交互预览需自行覆盖样式,并注意noClickStyles、表单提交拦截(Pattern 库在useEffect中preventDefault了所有form提交)等隔离手段是必要的配套工程; - 样式/脚本加载失败不阻塞:
loadStyles/loadScripts对单个资源失败采取"警告并继续"策略,预览可能会缺失部分效果但不会白屏; - 依赖范围:该库为 Calypso 内部包,peer 依赖包括
@wordpress/data、@wordpress/element、@wordpress/i18n、redux等,且大量使用@wordpress/block-editor的 unstable/private API(如__unstableIframe、__unstableEditorStyles、私有unlock),升级 Gutenberg 版本时需要特别留意兼容性。
八、总结
@automattic/block-renderer是 wp-calypso 中"在常规 React 页面还原 Gutenberg 区块真实效果"的标准化方案:BlockRendererProvider负责设置与全局样式的就绪,PatternsRendererProvider负责按类别批量获取渲染产物,PatternRenderer负责单图案的 HTML 加工与注入,BlockRendererContainer负责 iframe 隔离、等比缩放、资源加载与 Safari 兼容。它把"服务端渲染产出 + 客户端 iframe 呈现"的架构落地为可组合的 React 组件,其设计在 Pattern Library 的网格预览、站点搭建等场景中被反复复用。若你需要在 Calypso 系项目中构建区块/图案预览能力,直接按 README 的嵌套结构集成,再结合本文所述参数与数据流即可快速上手;若要深入调试,建议从 use-rendered-patterns.ts 与 block-renderer-container.tsx 这两个核心文件入手。
- 前端
- CMS
【免费下载链接】wp-calypso
The JavaScript and API powered WordPress.com
相关推荐
wp-calypso Reader 模块指南:路由体系、数据流与 Block 渲染开发详解
wp calypso Reader 模块指南:路由体系、数据流与 Block 渲染开发详解 Reader(阅读器)是 wp calypso 中承载 WordPr
前端CMSwp-calypso 中 AutomatticBylineLogo 组件:渲染「AN AUTOMATTIC AIRLINE」品牌 Byline 徽标的完整指南
wp calypso 中 AutomatticBylineLogo 组件:渲染「AN AUTOMATTIC AIRLINE」品牌 Byline 徽标的完整指南
前端CMS6 步做出自定义电商功能:Vendure 插件开发实战指南
6 步做出自定义电商功能:Vendure 插件开发实战指南 Vendure 是一个基于 TypeScript、NestJS 与 GraphQL 构建的无头(he
前端CMS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考