☰
WordPress.com Calypso 暂存站点状态 Reducer 深度解析:从 automated-transfer 到 React 状态树
2026/10/7 8:31:14 网站建设 项目流程
  • 前端
  • CMS

【免费下载链接】wp-calypso

The JavaScript and API powered WordPress.com

项目地址:https://gitcode.com/gh_mirrors/wp/wp-calypso
点击查看免费下载

暂存站点(Staging Site)是 WordPress.com 在发布前进行内容验证、代码测试与回滚演练的关键能力。本文聚焦 Calypso 前端状态管理库中负责「暂存站点自动化迁移/回滚状态」的 reducer 模块,围绕 client/state/staging-site/README.md 展开,逐层拆解状态值语义、reducer 数据流、selector 查询 API 与持久化机制。读完你将掌握如何基于stagingSite状态子树判断一次暂存站点创建(transfer)或删除(revert)是否正在进行、是否已完成,并能在自己的组件中安全、准确地消费这些状态。

一、模块定位:为「进行中的迁移」而生的独立 reducer

在 Calypso 庞大的client/state状态树中,stagingSite是一个刻意「瘦身」的 reducer:它只关心一件事——暂存站点的 automated-transfer(自动化迁移)状态。README 开宗明义地写道:"In this reducer, we store the automated-transfer status for the staging site."

之所以要从完整的 automated-transfer 状态中拆出独立 reducer,README 给出了两个核心动机:

  1. 数据简化:真实的后端迁移状态机非常复杂,而 UI 层真正关心的是start -> in progress -> end这一段线性序列,因此 reducer 只保留这一最小必要信息;
  2. 归属对象特殊:状态被写入生产站点(production site)ID之下,而不是暂存站点自身。原因在于迁移(transfer)一开始,暂存站点就处于不可用状态("the staging site is unavailable from the start of the transferring process"),客户端无法以暂存站点的 ID 为 key 去查询它的状态,只能把状态挂到生产站点上。

从源码结构看,该模块共由 6 个文件组成,职责划分清晰:

  • reducer.js:reducer 定义与持久化包装;
  • actions.js:唯一 action creatorsetStagingSiteStatus;
  • constants.ts:状态枚举StagingSiteStatus;
  • schema.js:持久化 JSON Schema 校验;
  • init.js:向 Redux store 注册 reducer;
  • selectors/:4 个状态查询 selector。

二、状态值语义:六种状态 + 一个空值

README 用一张表格完整定义了status字段的全部取值及其含义,这也是理解整个模块的钥匙。结合 constants.ts 的源码实现,各状态值整理如下:

StatusMeaning(README 原文语义)StagingSiteStatus 中的实际值
COMPLETERecords exist for a transfer, and it has previously finished. 存在迁移记录且此前已成功完成transferStates.COMPLETE
REVERTING后端返回信息表明暂存站点回滚(删除)流程正在进行'reverting'
REVERTED暂存站点已被成功删除transferStates.REVERTED
TRANSFERRING迁移至 Atomic(WordPress.com 托管运行时)正在进行中'transferring'
INITIATE_REVERTING回滚操作已发起(客户端尚无 automated-transfer 数据)'initiate reverting'
INITIATE_TRANSFERRING迁移操作已发起(客户端尚无 automated-transfer 数据)'initiate transferring'
falsey(如UNSET的'')Calypso 中不存在任何迁移相关信息''

值得注意的细节:COMPLETE与REVERTED直接复用calypso/state/automated-transfer/constants中transferStates的取值,而TRANSFERRING/REVERTING/ 两个INITIATE_*则是本模块自定义的字符串字面量。这正是 README 所说「把 transferStates 对象裁剪为只保留我们关心的状态」的代码体现——constants.ts 的注释进一步说明,状态对象到 transferStates 的映射发生在staging-site-card/index.js组件中。

从语义上可以把这些值分成三组:

  • 终态(Terminal):COMPLETE、REVERTED,以及代表「从未发生迁移」的UNSET('')与NONE——它们都表示当前没有进行中的操作;
  • 迁移进行态(Transferring):INITIATE_TRANSFERRING(客户端刚发起、尚无后端数据)→TRANSFERRING(后端确认迁移进行中);
  • 回滚进行态(Reverting):INITIATE_REVERTING→REVERTING。

三、Reducer 实现:keyedReducer + 状态持久化

reducer.js 的完整实现如下:

import { withStorageKey } from '@automattic/state-utils'; import { SITE_STAGING_STATUS_SET as SET_STATUS } from 'calypso/state/action-types'; import { combineReducers, keyedReducer, withSchemaValidation, withPersistence, } from 'calypso/state/utils'; import { StagingSiteStatus } from './constants'; import { stagingSite as schema } from './schema'; export const status = withPersistence( ( state = StagingSiteStatus.UNSET, action ) => { switch ( action.type ) { case SET_STATUS: return action.status || StagingSiteStatus.UNSET; default: return state; } } ); export const siteReducer = combineReducers( { status, } ); // state is a map of transfer sub-states // keyed by the associated site id const validatedReducer = withSchemaValidation( schema, keyedReducer( 'siteId', siteReducer ) ); const stagingSiteReducer = withStorageKey( 'stagingSite', validatedReducer ); export default stagingSiteReducer;

3.1 内层statusreducer

  • 默认状态为StagingSiteStatus.UNSET(即空字符串'',对应 README 表格中的falsey情形);
  • 仅响应SITE_STAGING_STATUS_SET一种 action 类型;
  • 处理逻辑为action.status || StagingSiteStatus.UNSET:当传入的status为假值时(如null、undefined、''),自动回落为UNSET,保证状态树中始终是一个可预期的字符串。

3.2 中层siteReducer与 keyedReducer

siteReducer通过combineReducers组合出{ status }形状;随后keyedReducer( 'siteId', siteReducer )以 action 中的siteId字段为 key,为每个站点生成一份独立的子状态。最终的状态树结构为:

stagingSite: { 12345: { status: 'transferring' }, 67890: { status: 'complete' }, ... }

这正好与 schema.js 中patternProperties: { '^\\d+$': ... }的约束对应——顶层对象的 key 必须是数字字符串形式的站点 ID,每个站点下的合法字段是status(字符串类型)。

3.3 外层持久化包装

  • withSchemaValidation( schema, ... ):在序列化/反序列化时用 schema.js 校验状态树,避免非法数据进入持久化存储;
  • withStorageKey( 'stagingSite', ... ):指定持久化存储 key 为stagingSite;
  • 最终由 init.js 调用registerReducer( [ 'stagingSite' ], stagingSiteReducer )注册进 Calypso 的 Redux store。

test/reducer.js 中的测试用例验证了这一持久化行为:以SITE_ID = 12345、status: StagingSiteStatus.COMPLETE构造状态,经serialize后root()序列化结果中该站点仍保留status字段;经deserialize反序列化后status依然等于COMPLETE——说明status是唯一需要持久化的键,且能完整往返。

四、Action 与 Action Type

模块只暴露一个 action creator actions.js:

import { SITE_STAGING_STATUS_SET } from 'calypso/state/action-types'; import 'calypso/state/staging-site/init'; export const setStagingSiteStatus = ( siteId, status ) => ( { type: SITE_STAGING_STATUS_SET, siteId, status, } );
  • type:SITE_STAGING_STATUS_SET,定义于 client/state/action-types.ts,由 reducer 中的SET_STATUS别名消费;
  • siteId:站点 ID。如前所述,这里应当传入生产站点的 ID,因为状态被 keyed 到生产站点之下;
  • status:新的状态值,类型为string | null(见 JSDoc),可为 README 表格中的任意值或null(null 会被 reducer 兜底为UNSET)。

在组件中典型用法形如dispatch( setStagingSiteStatus( siteId, StagingSiteStatus.TRANSFERRING ) ),用于在 UI 发起迁移/回滚操作时同步本地状态。

五、Selector 查询 API:如何读取状态

selectors/index.ts 统一导出了 4 个查询函数,全部通过getStagingSiteInfo( state, siteId )先取出某站点(生产站点 ID)的子状态,再作判断。它们也是组件消费该模块状态的标准入口。

5.1 getStagingSiteStatus —— 取原始状态值

  • 定义:get-staging-site-status.ts
  • 签名:getStagingSiteStatus( state, siteId ) => string | null
  • 语义:返回stagingSite[siteId].status的原始字符串;若siteId为空或该站点无任何记录,则回落为StagingSiteStatus.UNSET('')。对应 README 表格中falsey情形——Calypso 中不存在任何迁移信息。

5.2 getIsStagingSiteStatusComplete —— 是否处于「完成/空闲」态

  • 定义:get-is-staging-site-status-complete.ts
  • 签名:getIsStagingSiteStatusComplete( state, siteId ) => boolean
  • 语义:当状态为COMPLETE、REVERTED、NONE或UNSET之一时返回true。即:迁移已成功结束、站点已删除,或从未进行过迁移——UI 可以据此隐藏 loading 态、展示操作按钮。

5.3 getIsStagingSiteInTransition —— 是否处于「进行中」态

  • 定义:get-is-staging-site-in-transition.ts
  • 签名:getIsStagingSiteInTransition( state, siteId ) => boolean
  • 语义:当状态为INITIATE_TRANSFERRING、TRANSFERRING、INITIATE_REVERTING或REVERTING之一时返回true。这是判断「暂存站点正在创建或删除中」的关键 selector,通常用于禁用按钮、展示 spinner 等交互反馈。

getIsStagingSiteStatusComplete与getIsStagingSiteInTransition互为补集(NONE/UNSET归入前者),二者共同覆盖了 README 所述的完整状态序列:start(INITIATE_*)→ in progress(TRANSFERRING/REVERTING)→ end(COMPLETE/REVERTED)。

5.4 getStagingSiteInfo —— 底层取数

  • 定义:get-staging-site-info.ts
  • 签名:getStagingSiteInfo( state, siteId ) => Object
  • 语义:直接返回state.stagingSite[siteId];当siteId为假值或该站点无记录时返回空对象{}。其余三个 selector 都依赖它做空值兜底,因此组件内通常不需要直接调用。

六、数据流全景与实战接入要点

综合以上实现,一次「创建暂存站点」的完整状态流转在客户端可归纳为:

  1. UI 发起迁移 →dispatch( setStagingSiteStatus( productionSiteId, INITIATE_TRANSFERRING ) ),此时客户端尚无后端 automated-transfer 数据;
  2. 后端返回迁移进行中的信息 → 状态更新为TRANSFERRING;
  3. 迁移完成 → 状态更新为COMPLETE;
  4. 发起删除 →INITIATE_REVERTING→ 后端确认回滚中 →REVERTING→ 删除成功 →REVERTED。

回滚(删除)流程与之一一对称。

接入新组件时,建议遵循以下要点:

  • 用对 ID:所有 action 与 selector 传入的siteId都应是生产站点ID,而非暂存站点自身 ID(迁移期间暂存站点不可用,这是 README 反复强调的设计原因);
  • 优先用语义 selector:判断「能否操作」用getIsStagingSiteStatusComplete,判断「正在操作」用getIsStagingSiteInTransition,仅在需要展示精确文案时才读取getStagingSiteStatus的原始值;
  • 借助模块化状态规范:模块遵循 Calypso 的 modularized state 约定——actions.js、selectors/index.ts均以import 'calypso/state/staging-site/init'开头,确保按需加载时 reducer 已被注册;
  • 持久化安全:status经 schema.js 校验后持久化,非法结构会被过滤,读取侧无需额外防御非法数据。

七、小结

stagingSitereducer 用最小的状态面(一个status字段)支撑了暂存站点创建/删除全生命周期的 UI 呈现,其设计精髓在于:面向 UI 需要而非后端全量状态、按生产站点 ID 建索引、语义化 selector 屏蔽内部细节。阅读 README 掌握状态语义表,结合 constants.ts、reducer.js 与 selectors 理解实现,再对照 test/reducer.js 验证持久化行为,即可完整掌握该模块并在实际组件中正确使用。

  • 前端
  • CMS

【免费下载链接】wp-calypso

The JavaScript and API powered WordPress.com

项目地址:https://gitcode.com/gh_mirrors/wp/wp-calypso
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询