Gutenberg 站点编辑器路由系统解析:Site Editor Routes 的 Areas 与 canvasMode 机制
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
站点编辑器(Site Editor)的左侧导航、内容面板、预览画布与移动端布局,全部由一个基于「路由 + Areas」的组合式框架驱动。本文以 packages/edit-site/src/components/site-editor-routes/README.md 为核心,结合该目录下全部路由定义、store 层的注册动作与 layout 渲染源码,完整讲解 Site Editor Routes 的组成结构、各 Area 在不同视口与canvasMode下的渲染规则,以及如何阅读与扩展一条新路由。
读完本文,你将掌握:一条站点编辑器路由由哪些字段构成、sidebar/content/preview/edit/mobileSidebar/mobileContent六个 Area 各自的职责与优先级、canvasMode切换为edit时布局如何收敛为全屏画布,以及这套机制在首页、样式、模板、页面、图案、导航等真实路由中如何落地。
一、为什么需要「Site Editor Routes」
站点编辑器是一个复杂的多页面应用,它同时承载了:
- 站点首页(
/)的概览导航; - 样式(Styles)与 Stylebook 预览;
- 模板(Templates)、模板部件(Template Parts)、页面(Pages)的浏览与编辑;
- 图案(Patterns)与导航(Navigation)的管理;
- 以及一条兜底的 404 路由。
传统做法是在一个巨型组件里用条件渲染堆叠所有页面,而 Gutenberg 的做法是把每个页面抽象为一条「路由」,每条路由声明自己需要占据哪些「区域(Areas)」,再由统一的 Layout 组件根据**视口宽度(是否移动端)**与canvasMode(编辑画布模式)决定每个区域最终渲染在哪里、以什么形式出现。
这套体系的核心代码集中在 site-editor-routes 目录,共定义了 14 条路由(home、identity、styles、navigation、navigation-item、patterns、pattern-item、template-part-item、templates、template-item、pages、page-item、stylebook、notfound),而 layout/index.jsx 负责最终将这些 Areas 摆放到屏幕上。
二、Areas:路由声明的六个渲染区域
在 README 中,一个关键概念是Area。每条路由可以通过areas字段声明最多六个区域,每个区域都是一个 React 节点或返回节点的函数。这些区域并非都会被渲染,是否渲染取决于canvasMode与视口宽度。
2.1canvasMode为非edit时的渲染规则
当canvasMode不是edit(即处于浏览/导航模式)时,可用区域及其渲染行为如下表(原文完整继承):
| Area | 非移动端视口 | 移动端视口 |
|---|---|---|
sidebar | 始终渲染。 | 仅当未提供任何移动端区域(mobileSidebar、mobileContent或preview)时渲染。 |
content | 提供了才渲染。 | 不渲染。 |
preview | 提供了才渲染。 | 不渲染。 |
edit | 提供了才渲染。 | 不渲染。 |
mobileSidebar | 不渲染 | 提供了且未提供mobileContent时,以全屏方式渲染(使用主题化的侧边栏背景)。 |
mobileContent | 不渲染 | 提供了则渲染,全屏显示(使用白色内容背景)。 |
要点提炼:
sidebar是唯一「始终渲染」的桌面端区域,它承载左侧导航栏;content、preview、edit在桌面端都是「可选渲染」的补充区域,用于承载中间列表或右侧画布;- 移动端是「二选一」结构:有
mobileContent时优先显示内容,否则回退到mobileSidebar;两者都缺省时才回退到桌面端sidebar。
2.2canvasMode为edit时的渲染规则
当canvasMode是edit(进入某个实体的编辑画布)时,布局收敛为全屏编辑模式:
| Area | 非移动端视口 | 移动端视口 |
|---|---|---|
preview | 提供了则全屏渲染;否则显示一个空白侧边栏。 | 提供了则全屏渲染;否则显示一个空白侧边栏。 |
也就是说,进入canvasMode=edit后,sidebar、content、edit均不再按浏览模式渲染,只剩preview一个区域独占整个画布——这正是用户点击某个模板、页面或图案后看到的「全屏编辑器」形态。
三、路由的结构:name、path、areas 与 widths
每一条路由都是导出为一个普通对象。以 home.jsx 为例,其结构为:
export const homeRoute = { name: 'home', path: '/', areas: { sidebar( { siteData } ) { ... }, preview( { siteData } ) { ... }, mobileSidebar( { siteData } ) { ... }, }, };字段说明:
| 字段 | 作用 |
|---|---|
name | 路由唯一标识,用于注册与注销,也作为routeKey参与布局渲染。 |
path | 路由路径。支持静态路径(/styles)、路径参数(/page/:postId)以及通配段(/wp_template/*postId、/template),并支持*通配符作为 404 兜底(见 notfound.jsx)。 |
areas | 一个对象,键为上述六个 Area 名,值为 React 元素或( { siteData, query } ) => ReactNode形式的函数。函数形式可以拿到站点数据与 URL 查询参数,按需返回null/undefined表示「不渲染」。 |
widths | 可选,用于指定各 Area 的宽度,例如content: 380(像素),也支持异步函数动态计算宽度。 |
3.1 常见路径模式一览
从源码中可以整理出全部路由的路径定义:
| 路由 | path | 说明 |
|---|---|---|
home | / | 站点首页 |
identity | /identity | 站点身份(logo、标题等) |
styles | /styles | 全局样式 |
stylebook | /stylebook | 经典主题下的 Stylebook 预览 |
navigation | /navigation | 导航菜单列表 |
navigation-item | /wp_navigation/:postId | 单个导航菜单编辑 |
patterns | /pattern | 图案列表 |
pattern-item | /wp_block/:postId | 单个图案编辑 |
templates | /template | 模板列表 |
template-item | /wp_template/*postId | 单个模板编辑 |
template-part-item | /wp_template_part/*postId | 单个模板部件编辑 |
pages | /page | 页面列表 |
page-item | /page/:postId | 单个页面编辑 |
notfound | * | 404 兜底 |
可以看到:内容实体(页面、模板、图案、导航)遵循「列表路径为/xxx、详情路径为/xxx/:postId」的约定,其中模板与模板部件因实体 ID 可能包含斜杠,使用了*postId通配捕获段。
四、路由注册:从模块导出到 store
路由对象在 index.js 中汇总为routes数组,并通过useRegisterSiteEditorRoutes()这个 Hook 完成注册:
export function useRegisterSiteEditorRoutes() { const registry = useRegistry(); const { registerRoute } = unlock( useDispatch( siteEditorStore ) ); useEffect( () => { registry.batch( () => { routes.forEach( registerRoute ); } ); }, [ registry, registerRoute ] ); }关键细节:
registerRoute是@wordpress/datastore 的私有 action,定义于 private-actions.js,通过unlock()解锁后使用;- 注册动作
REGISTER_ROUTE在 reducer.js 中把路由对象追加到state.routes数组;对应的unregisterRoute通过UNREGISTER_ROUTE按name过滤移除; - 读取端使用私有 selector
getRoutes(见 private-selectors.js),在 app/index.jsx 中通过unlock( select( editSiteStore ) ).getRoutes()取得全部路由,作为routes属性传入<RouterProvider>,交给@wordpress/router做匹配与分发。
由于注册发生在useEffect中,且整批路由一次性批量注册,因此路由表在应用挂载后即完整可用,这与「路由匹配-渲染」的解耦设计是一致的。
五、Layout:Areas 的最终摆布与 canvasMode 语义
路由只负责「声明」,真正的布局逻辑在 layout/index.jsx 中。结合源码可以验证 README 中的每一条规则:
5.1 桌面端布局
sidebar始终渲染在左侧(areas.sidebar,被包裹在SidebarNavigationProvider中,见 layout/index.jsx);content仅在非移动端且canvas !== 'edit'时渲染(! isMobileViewport && areas.content && canvas !== 'edit'),并使用widths?.content控制最大宽度;edit区域同样仅在非移动端且canvas !== 'edit'时渲染;preview在非移动端渲染于画布容器edit-site-layout__canvas-container中,支持可拖拽的canvasResizer(layout/index.jsx);- 当
canvas === 'edit'时,布局类名追加is-full-canvas,导航侧栏被隐藏,preview区域独占全屏。
5.2 移动端布局
移动端分支(isMobileViewport && hasMobileAreas)验证了 README 的移动规则:
- 优先渲染
areas.mobileContent,且外层套上ThemeProvider color={ CONTENT_COLOR }(白色内容背景); - 若未提供
mobileContent,则回退渲染areas.mobileSidebar(主题化侧边栏背景); - 两者都未提供时,桌面端的
sidebar才会作为兜底出现; - 当
canvas === 'edit'时,移动端同样直接渲染areas.preview,且以白色内容背景包裹。
5.3canvasMode从哪来
canvasMode(源码中通常以canvas表示,取值为'edit'或其他浏览模式)来自 URL 查询参数,通过@wordpress/router的useLocation()读取。源码中有多处将其置为edit的操作,例如 sidebar-navigation-screen-global-styles/index.jsx 中history.navigate( addQueryArgs( path, { canvas: 'edit' } ) ),以及 block-editor/use-editor-iframe-props.js 中的相关跳转。这解释了「从浏览视图点击某个模板/页面 → 进入全屏编辑」这一交互链路。
六、真实路由逐条拆解
6.1 首页路由(home)
home.jsx 是最能体现 Areas 动态性的例子。它的三个区域都依赖siteData异步判断:
sidebar在主题数据未加载时返回null(等待数据);- 主题为块主题或经典主题支持 Stylebook 时渲染
SidebarNavigationScreenMain,否则渲染SidebarNavigationScreenUnsupported(不支持提示); preview仅在支持的条件下渲染<Editor isHomeRoute renderingMode="template-locked" />,否则返回undefined(画布留空);mobileSidebar与sidebar逻辑一致。
这里的siteData来自路由 area 的 resolver 提供的站点数据,辅助函数定义在 utils.js:
isThemeDataLoaded( siteData ):通过currentTheme是否存在判断 REST 主题数据是否已加载;isClassicThemeWithStyleBookSupport( siteData ):判断经典主题是否支持 Stylebook——即!is_block_theme && ( theme_supports['editor-styles'] || supportsLayout )。
6.2 样式路由(styles)与 Stylebook
styles.jsx 同时用到了content与mobileContent:
areas: { content: <SidebarGlobalStyles />, sidebar: <SidebarNavigationScreenGlobalStyles backPath="/" />, preview: ( { siteData } ) => <StylesPreviewArea siteData={ siteData } />, mobileContent: <SidebarGlobalStyles />, }, widths: { content: 380 },- 桌面端:左侧
sidebar(全局样式导航)+ 中间content(全局样式设置面板,宽度 380px)+ 右侧preview画布; - 移动端:直接用
mobileContent全屏展示全局样式设置; StylesPreviewArea会根据 URL 查询参数preview=stylebook决定画布渲染 Stylebook 还是编辑器;StyleBookPreviewArea还会从query.section读取当前浏览的区块分类,并调用useGlobalStyles()传入userConfig,使未保存的全局样式修改能实时反映到预览中。
同样与 Stylebook 相关的还有 stylebook.jsx:它专为「经典主题但支持 Stylebook」的场景服务,桌面端在preview中渲染StyleBookPreview isStatic,移动端则在mobileContent中渲染同样的静态预览。
6.3 模板与页面列表(templates / pages)
template-item.jsx 与 pages.jsx 展示了异步 Area 与动态宽度的用法:
preview区域是async函数:它通过@wordpress/views的loadView()读取当前activeView对应的视图配置(view_list中匹配activeView的view),只有当视图类型为list时才返回<Editor />(此时桌面端将 sidebar+content 与画布并排),否则返回undefined;widths.content同样声明为async,依据视图是否为列表动态返回380(列表宽度)或undefined(不限制)——例如 templates.jsx;- 内容区域(
content/mobileContent)在块主题下渲染PageTemplates或PostList,非块主题则返回undefined并让sidebar展示SidebarNavigationScreenUnsupported; - 单个实体的编辑路由(如 template-item.jsx、page-item.jsx、pattern-item.jsx、navigation-item.jsx、template-part-item.jsx)结构高度一致:
sidebar提供返回列表的导航,preview直接渲染<Editor />进入全屏编辑(源码注释明确写到 "Also rendered on mobile, where this route is only reached at canvas=edit",印证了 README 中「edit 模式下仅preview渲染」的规则)。
6.4 兜底路由(notfound)
notfound.jsx 使用path: '*'捕获所有未匹配路径,在sidebar/mobileSidebar/content中分别渲染错误提示NotFoundError(基于Notice status="error"),保证站点编辑器在任何无效 URL 下都有友好的反馈。
七、如何新增或修改一条路由
基于以上机制,在站点编辑器中接入新页面的一般步骤为(仅说明查看与理解方式,仓库为只读):
- 在
packages/edit-site/src/components/site-editor-routes/下新建一个路由模块,导出{ name, path, areas }对象; - 按需求声明
areas:桌面浏览模式至少提供sidebar;如需中间面板补充content;如需画布预览补充preview;移动端体验考虑补充mobileContent或mobileSidebar; - 若需要固定宽度面板(如 380px 的设置栏),在
widths.content中声明; - 在 index.js 的
routes数组中追加该路由对象,useRegisterSiteEditorRoutes()会在应用启动后自动将其注册进editSiteStore; areas中的函数参数{ siteData, query }可用于主题能力判断(如is_block_theme)与 URL 查询参数分支(如query.preview === 'stylebook'、query.categoryId),在数据未就绪时应返回null等待加载。
八、总结
Site Editor Routes 的「路由 + Areas」抽象,把站点编辑器的多页面布局问题拆解成了清晰的三层:路由层(name/path/areas/widths的声明)、注册层(通过registerRoute私有 action 进入 store 的routes状态)、渲染层(Layout 依据isMobileViewport与canvas决定每个 Area 的最终位置与形态)。README 中关于canvasMode与移动端的六行规则,在 layout/index.jsx 中都有对应的源码分支与之严格对应,是本项目响应式站点编辑器架构最值得研读的起点。
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考