ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

wp-calypso 数据组件深度解析:用 QuerySiteDomains 声明式拉取站点域名列表

wp-calypso 数据组件深度解析:用 QuerySiteDomains 声明式拉取站点域名列表 wp-calypso 数据组件深度解析用 QuerySiteDomains 声明式拉取站点域名列表【免费下载链接】wp-calypsoThe JavaScript and API powered WordPress.com项目地址: https://gitcode.com/gh_mirrors/wp/wp-calypsoQuerySiteDomains /是 wp-calypsoWordPress.com 的 JavaScript 前端中负责站点域名列表这一数据请求的 React 组件只要把它渲染到页面上并传入siteId它就会自动发起对sites/%s/domains接口的请求并把结果写入 Redux 状态树。本文以 client/components/data/query-site-domains/README.md 为骨架结合其组件源码、Redux action / selector / reducer 以及真实业务调用场景完整还原这一数据查询组件从渲染到状态落地的整条链路读完即可在自己的业务页面中正确使用它并理解其去重与防抖设计。一、组件定位wp-calypso 的数据查询组件模式在 wp-calypso 中client/components/data/目录下聚集了一类特殊的组件它们不渲染任何 DOM唯一的职责是替页面发起数据请求。QuerySiteDomains正是其中的一员README 的第一句就点明了它的使命QuerySiteDomains /is a React component used in managing network requests for sites/%s/domains.也就是说它管理的是对 WordPress.com REST APIsites/{siteId}/domains这一端点的请求。与之配套的还有query-site、query-site-purchases等兄弟组件共同构成 Calypso谁需要数据谁就挂一个 Query 组件的声明式数据获取范式业务组件不再自己写fetch而是把数据需求声明出来由 Query 组件集中处理请求生命周期。二、基本用法渲染组件、传入 siteIdREADME 给出的使用方式非常简洁渲染组件并传入siteId组件不接受任何 children也不会向页面渲染任何元素。文档中的示例Class 组件风格如下import QuerySiteDomains from calypso/components/data/query-site-domains; class MyComponent extends React.Component { render() { const { site, domains } this.props; return ( div QuerySiteDomains siteId{ site.ID } / ul { domains.map( ( domain ) { return li{ domain.domain }/li; } ) } /ul /div ); } }这里有一个容易被忽略的关键点QuerySiteDomains /并不负责把数据吐给子组件。它只是把请求触发出去数据最终存放在 Redux store 中页面通过connect或 selector如getDomainsBySiteId从 store 里读取domains再自行渲染。所以示例中domains来自this.props而不是来自组件的 children 或渲染输出。基于同一模式在现代 Hooks 风格下可以这样组合使用import { useDispatch, useSelector } from react-redux; import QuerySiteDomains from calypso/components/data/query-site-domains; import { getDomainsBySiteId } from calypso/state/sites/domains/selectors; function MyDomainsList( { siteId } ) { const domains useSelector( ( state ) getDomainsBySiteId( state, siteId ) ); return ( div QuerySiteDomains siteId{ siteId } / ul { domains.map( ( domain ) ( li key{ domain.domain }{ domain.domain }/li ) ) } /ul /div ); }三、组件源码拆解useEffect 驱动的请求触发只看 README 无法了解组件的内部行为真正实现位于同目录下的 client/components/data/query-site-domains/index.jsx。完整源码只有 24 行却包含了几个值得注意的设计import PropTypes from prop-types; import { useEffect } from react; import { useDispatch } from react-redux; import { fetchSiteDomains } from calypso/state/sites/domains/actions; import { isRequestingSiteDomains } from calypso/state/sites/domains/selectors; const request ( siteId ) ( dispatch, getState ) { if ( siteId ! isRequestingSiteDomains( getState(), siteId ) ) { dispatch( fetchSiteDomains( siteId ) ); } }; export default function QuerySiteDomains( { siteId } ) { const dispatch useDispatch(); useEffect( () { dispatch( request( siteId ) ); }, [ dispatch, siteId ] ); return null; } QuerySiteDomains.propTypes { siteId: PropTypes.number.isRequired };可以提炼出四个要点函数式组件 Hooks组件用useDispatch拿到 Redux 的dispatch在useEffect中触发请求依赖数组为[ dispatch, siteId ]——当siteId变化时旧站点的请求会被清理、新站点会被重新请求。返回null组件不渲染任何 DOM与 README 中does not render any elements to the page的描述完全一致。请求去重request是一个 thunk它在dispatch前先通过isRequestingSiteDomains( getState(), siteId )检查该站点是否已经在请求中如果已有一个进行中的请求就跳过避免重复打接口。这是数据查询组件模式的防重设计。空值保护siteId为假值undefined/null/0时直接跳过请求因此可以放心地在站点尚未加载完成的场景下无条件渲染该组件。propTypes声明siteId为必填的 number 类型。四、底层数据流从 thunk 到 REST API 再到 Redux组件 dispatch 的fetchSiteDomains定义在 client/state/sites/domains/actions.js它是整条链路的核心。简化后的实现如下export function fetchSiteDomains( siteId ) { return ( dispatch ) { dispatch( domainsRequestAction( siteId ) ); return wpcom.req .get( /sites/${ siteId }/domains, { apiVersion: 1.2 } ) .then( ( data ) { const { domains [], error, message } data; if ( error ) { throw new Error( message ); } dispatch( domainsRequestSuccessAction( siteId ) ); dispatch( domainsReceiveAction( siteId, domains ) ); } ) .catch( ( error ) { const message error instanceof Error ? error.message : translate( There was a problem fetching site domains. Please try again later or contact support. ); dispatch( domainsRequestFailureAction( siteId, message ) ); } ); }; }一次完整请求的生命周期由四类 action 标记类型定义见 client/state/action-types.js 中的SITE_DOMAINS_*常量Action触发时机含义SITE_DOMAINS_REQUEST请求发出前将该站点标记为请求中SITE_DOMAINS_REQUEST_SUCCESS接口成功返回清除请求中标记SITE_DOMAINS_RECEIVE成功返回且无 error 字段把组装好的域名数组写入 stateSITE_DOMAINS_REQUEST_FAILURE请求失败或响应带 error记录错误信息几个实现细节值得注意API 版本请求使用apiVersion: 1.2这是 wpcom.jscalypso/lib/wp对sites/{siteId}/domains端点约定的版本参数。业务错误处理即使 HTTP 请求成功响应体中若带有error字段如{ error: ..., message: ... }同样会被视为失败并抛出new Error( message )。错误信息本地化兜底错误文案通过i18n-calypso的translate()提供符合 Calypso 全局的国际化要求。响应数据组装domainsReceiveAction内部对每个原始域名调用createSiteDomainObject见下文完成字段归一化后再入库。从代码结构看这一套REQUEST → SUCCESS/FAILURE/RECEIVE的动作组是整个state/sites/domains模块的基础同文件中的setPrimaryDomain在切换主域名后也会调用fetchSiteDomains( siteId )刷新列表见 actions.js 中的setPrimaryDomain说明该 thunk 既是 Query 组件的数据入口也是业务操作后刷新数据的公共手段。五、状态落地reducer 与 selector 的配合5.1 Redux 状态结构请求结果被 client/state/sites/domains/reducer.js 中的combineReducers组织为五个切片export default combineReducers( { errors, items, requesting, updatingPrivacy, updatingPrimaryDomain, } );与本文主题直接相关的是items与requesting两个切片items以siteId为 key、域名数组为 value 的映射。SITE_DOMAINS_RECEIVE时通过Object.assign( {}, state, { [ siteId ]: action.domains } )不可变地写入它还用withSchemaValidation包了一层配合 client/state/sites/domains/schema.js 做持久化时的数据校验。此外DOMAIN_PRIVACY_ENABLE_SUCCESS、DOMAIN_DNSSEC_ENABLE_SUCCESS、DOMAIN_DETAILS_RECEIVE等业务 action 也会通过modifySiteDomainObjectImmutable局部更新某个域名对象而无需重新请求。requesting以siteId为 key 的布尔标记。SITE_DOMAINS_REQUEST置为trueSITE_DOMAINS_REQUEST_SUCCESS/SITE_DOMAINS_REQUEST_FAILURE置为false。这正是组件去重逻辑依赖的数据源。errors以siteId为 key 的错误信息SITE_DOMAINS_REQUEST_FAILURE时写入下一次请求开始时清空。5.2 常用 selectorclient/state/sites/domains/selectors.js 提供了读取这些状态的官方入口getDomainsBySiteId( state, siteId )返回某站点的域名数组siteId为空或该站点尚未加载时返回EMPTY_SITE_DOMAINS一个Object.freeze( [] )的共享空数组保证多次调用的返回值引用相等避免引起不必要的重渲染。getDomainsBySite( state, site )接受 site 对象取site.ID的便捷封装。getWpComDomainBySiteId( state, siteId )从列表中挑出 WordPress.com 自带域名isWPCOMDomain或isWpcomStagingDomain。hasLoadedSiteDomains( state, siteId )判断该站点的域名列表是否已加载完成items[siteId]是否存在。isRequestingSiteDomains( state, siteId )判断是否正在请求Query 组件内部即使用它做请求去重。六、数据归一化createSiteDomainObject 组装 ResponseDomain接口返回的域名对象是 snake_case 的原始结构而业务代码消费的是 camelCase 的领域对象。这一转换由 client/state/sites/domains/assembler.js 的createSiteDomainObject完成domainsReceiveAction会把它逐一应用到响应数组上。该函数将原始字段映射为统一形态的ResponseDomain对象类型定义参见 client/lib/domains/types.ts覆盖了域名管理所需的几乎所有维度例如基础标识domain/name均为域名本身、blogId、type通过getDomainType判定如mapping、wpcom、transfer等、isSubdomain、isPrimary来自primary_domain生命周期registrationDate、expiry、expirySoon、autoRenewing、renewableUntil、redeemableUntil所有权与权限currentUserIsOwner、currentUserCanManage、canSetAsPrimary、canManageDnsRecords、canUpdateContactInfo及对应的cannot*Reason字段隐私与合规privateDomain、privacyAvailable、contactInfoDisclosed、gdprConsentStatus经getGdprConsentStatus计算技术状态isDnssecEnabled/dnssecRecords经assembleDnssecRecords拆出dnskey与dsData、hasWpcomNameservers、pointsToWpcom、sslStatus转移相关transferStatus经getTransferStatus计算、transferStartDate、transferEndDate由transfer_start_date加 7 天推算、pendingTransfer、isEligibleForInboundTransfer邮件与订阅googleAppsSubscription/titanMailSubscription键统一转 camelCase、emailForwardsCount、productSlug、subscriptionId。值得注意的是组装过程做了大量类型收窄多数布尔字段用Boolean(...)强制归一日期字段统一转为String或null避免 API 返回undefined或数字导致下游类型混乱。这正是数据查询组件模式的价值延伸——把不稳定的外部响应在入口处清洗成内部可信的领域模型。七、真实业务场景组件在 Calypso 中的落地QuerySiteDomains并非孤立示例它被多个真实业务页面复用。以域名管理模块为例在 client/my-sites/domains/domain-management/edit-contact-info-page/bulk-edit-contact-info-page.tsx 中批量编辑联系信息页面会根据首个选中域名所属站点blog_id渲染QuerySiteDomains siteId{ firstSelectedDomain.blog_id } /以确保后续对域名列表如contactInfoDisclosed等字段的读取有最新的 store 数据支撑。在 client/my-sites/domains/domain-search/index.tsx 中域名搜索页在selectedSite?.ID存在时渲染QuerySiteDomains siteId{ selectedSite.ID } /让搜索页能够同步展示当前站点已绑定的域名状态。这两个场景体现了一个共同的使用契约当页面需要在站点尚未完全就绪时就声明数据需求时把 Query 组件无条件挂载、用siteId的可用性做守卫即可——组件内部自己处理空值与去重业务组件无需关心请求时机。八、使用要点与最佳实践小结综合 README、组件源码与底层实现在实际业务中使用QuerySiteDomains时建议遵循以下约定声明式挂载在需要域名数据的组件中直接渲染QuerySiteDomains siteId{ site.ID } /不要手动触发fetchSiteDomains除非是setPrimaryDomain这类业务操作后的主动刷新。数据从 store 读取用getDomainsBySiteId/getDomainsBySite读取域名数组用hasLoadedSiteDomains判断是否加载完成、isRequestingSiteDomains判断是否请求中从而决定是否展示 loading 占位。渲染与请求解耦组件返回null、不接受 children页面布局完全由业务组件自己控制。安全守卫siteId为必填 number当站点对象尚未加载时用site?.ID或selectedSite?.ID之类的可选链传参组件内部对空值做了保护。复用而非重复请求同一siteId的多个页面同时挂载该组件时isRequestingSiteDomains会拦截重复请求请求完成后数据已入 store后续挂载无需重新拉取。相关源码导航组件实现client/components/data/query-site-domains/index.jsx组件文档client/components/data/query-site-domains/README.mdAction 定义client/state/sites/domains/actions.jsSelector 定义client/state/sites/domains/selectors.jsReducer 定义client/state/sites/domains/reducer.js数据组装client/state/sites/domains/assembler.js模块状态说明client/state/sites/domains/README.md真实使用案例bulk-edit-contact-info-page.tsx、domain-search/index.tsx【免费下载链接】wp-calypsoThe JavaScript and API powered WordPress.com项目地址: https://gitcode.com/gh_mirrors/wp/wp-calypso创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表