Elementor Query 包(@elementor/query):基于 TanStack Query 的编辑器数据请求封装与版本演进全解
2026/9/17 19:28:44 网站建设 项目流程

Elementor Query 包(@elementor/query):基于 TanStack Query 的编辑器数据请求封装与版本演进全解

【免费下载链接】elementorThe most advanced frontend drag & drop page builder. Create high-end, pixel perfect websites at record speeds. Any theme, any page, any design.项目地址: https://gitcode.com/GitHub_Trending/el/elementor

在 Elementor 现代编辑器(Editor One)的 React 体系中,服务端状态管理统一收敛在一个轻量封装包@elementor/query上。本文以 packages/packages/libs/query/CHANGELOG.md 的版本演进为主轴,结合 包源码、README 与编辑器内的真实调用链,完整梳理该包的 API 设计、单例 QueryClient 管理机制、默认请求策略,以及如何在业务代码中落地分页、缓存失效与数据更新。读完本文,你将掌握 Elementor 内部服务端状态的标准写法和从版本记录反推演进脉络的方法。

一、从 CHANGELOG 看包的整体定位

@elementor/query是一个围绕@tanstack/react-query的薄封装(wrapper),目标是把 TanStack Query 的能力以受控的方式暴露给 Elementor 编辑器。它的完整版本历史记录在 packages/packages/libs/query/CHANGELOG.md,采用 Conventional Commits 规范维护,日志按时间倒序排列。

按时间线梳理版本演进:

版本日期关键变更
0.1.02023-06-28首次发布:query包新增对 React Query 的支持(issue [ED-11193],PR #62)
0.1.1 ~ 0.1.62023-06 ~ 2023-11仅版本号 bump(Version bump only),无功能性变更
0.2.02024-01-29新特性:为editor-site-navigation的 pages 面板引入分页(pagination)能力(PR #154)
0.2.1 ~ 0.2.22024-07 ~ 2024-08版本号 bump
0.2.3修复package.jsonexports字段(Fix package.json exports field)
0.2.4更新依赖(Update dependencies)

CHANGELOG 揭示出两个事实:其一,该包是一个平台底座型依赖,多数版本只是跟随上游更新;其二,真正影响业务的是 0.2.0 引入的分页支持和 0.2.3 对exports字段的修复——后者直接关系到包在 ESM/CJS 双格式下的导入稳定性。

二、核心 API:源码级解读

包的实现非常精简,全部逻辑集中在 packages/packages/libs/query/src/index.ts 一个文件中:

import { QueryClient } from '@tanstack/react-query'; export { useQuery, useInfiniteQuery, useMutation, useIsMutating, useQueryClient, QueryClient, QueryClientProvider, type UseQueryResult, } from '@tanstack/react-query'; let queryClient: QueryClient | undefined; export function getQueryClient(): QueryClient { if ( ! queryClient ) { throw new Error( 'Query client is not created yet.' ); } return queryClient; } export function createQueryClient() { if ( queryClient ) { throw new Error( 'Query client is already created.' ); } queryClient = new QueryClient( { defaultOptions: { queries: { refetchOnWindowFocus: false, refetchOnReconnect: false, }, }, } ); return queryClient; }

2.1 全量复出口:Hooks 与类型

包直接从@tanstack/react-query复出(re-export)了业务层最常用的 API:

  • useQuery/useInfiniteQuery:查询数据,后者支持分页游标;
  • useMutation:执行写入型操作(创建、更新、删除);
  • useIsMutating:观察当前是否有进行中的 mutation;
  • useQueryClient:在组件树中获取当前QueryClient实例;
  • QueryClient/QueryClientProvider:客户端实例类型与 Provider 组件;
  • type UseQueryResult:查询返回值的类型。

业务代码只需从@elementor/query导入,而不必直接依赖@tanstack/react-query,从而将第三方库锁定在包的内部,便于统一升级与替换。

2.2 单例 QueryClient:create 与 get 的成对约束

模块级变量let queryClient维护了一个进程级单例:

  • createQueryClient()首次创建实例;若已存在则抛出'Query client is already created.',保证整个编辑器只存在一个客户端;
  • getQueryClient()返回已创建的实例;若尚未创建则抛出'Query client is not created yet.',用于在 React 组件树之外(如纯工具函数、事件回调)安全访问客户端。

这种“创建一次、全局复用”的设计与 React 应用中每个 Provider 各自 new 一个 Client 的常见写法不同,目的是在编辑器这种长期存活的 SPA 环境中统一缓存状态、避免多实例导致的缓存数据分叉。

2.3 默认策略:关闭窗口聚焦与断线重连时的自动刷新

createQueryClient()通过defaultOptions.queries为所有查询设置了两个全局默认值:

  • refetchOnWindowFocus: false:窗口重新获得焦点时不自动重新请求;
  • refetchOnReconnect: false:网络恢复时不自动重新请求。

从源码结构看,这是针对编辑器场景的刻意取舍:编辑器面板数据以用户主动操作为主,关闭自动刷新可避免拖拽、输入过程中因窗口焦点变化引发意外请求。业务层仍可通过useQuery的局部选项覆盖这些默认值,例如 use-user.ts 中显式设置了staleTime: 30 * 60 * 1000,让用户信息在 30 分钟内直接命中缓存。

三、使用指南:从 README 示例到完整可运行代码

README 给出了最简用法:创建客户端 → 用QueryClientProvider注入 → 在组件中调用useQuery。下面将其扩写为完整可复制的示例:

import { createQueryClient, QueryClientProvider, useQuery } from '@elementor/query'; const queryClient = createQueryClient(); const App = () => ( <QueryClientProvider client={ queryClient }> <MyComponent /> </QueryClientProvider> ); const MyComponent = () => { const { data: todos, isLoading } = useQuery( { queryKey: 'todos', queryFn: () => fetch( '/todos' ).then( ( res ) => res.json() ), } ); if ( isLoading ) { return <div>Loading...</div>; } return todos.map( ( todo ) => <div key={ todo.id }>{ todo.title }</div> ); };

要点说明:

  • queryKey支持字符串或数组。从编辑器现有代码看,团队惯例是使用命名空间数组,例如[ 'site-navigation', 'posts', postTypeSlug ](见 use-posts.ts),便于按前缀批量失效;
  • queryFn可以是任意返回 Promise 的函数,不限于fetch,编辑器内大量使用自定义的getRequest/getSettings/getUser等 API 封装函数;
  • 包声明了peerDependencies: { "react": "^18.3.1" },使用时需确保宿主环境为 React 18.3 及以上。

3.1 内部约定:__前缀不可依赖

README 特别以警告块(> [!WARNING])声明:凡是以双下划线__开头的函数或变量均视为内部实现,可能在任何版本中无通知变更,第三方开发者不得访问或依赖。这一约定属于包 API 稳定性的“红线”,在升级依赖前应检查代码中是否引用了此类符号。

四、编辑器内的真实调用链:分页、缓存失效与写入

CHANGELOG 0.2.0 提到的“pages 面板分页”特性,其实现正落在editor-site-navigation模块中,是理解该包实战价值的最佳样本。

4.1 无限滚动分页:useInfiniteQuery

use-posts.ts 完整演示了分页数据流的四个要素:

export function usePosts( postTypeSlug: Slug ) { const query = useInfiniteQuery( { queryKey: postsQueryKey( postTypeSlug ), queryFn: ( { pageParam = 1 } ) => getRequest( postTypeSlug, pageParam ), initialPageParam: 1, getNextPageParam: ( lastPage ) => { return lastPage.currentPage < lastPage.totalPages ? lastPage.currentPage + 1 : undefined; }, } ); return { ...query, data: { posts: flattenData( query.data ), total: query.data?.pages[ 0 ]?.totalPosts ?? 0 } }; }
  • initialPageParam: 1声明起始页码;
  • getNextPageParam依据当前页与总页数的比较返回下一页,返回undefined时表示没有更多数据;
  • 最后通过flattenData把分页的pages数组拍平为帖子列表,并暴露total总数——这是对useInfiniteQuery返回结构的一次典型适配封装。

4.2 查询 + 失效:useQuery 与 invalidateQueries 的配对

读侧与写侧通过相同的queryKey约定联动:

  • use-homepage.ts 用settingsQueryKey() => [ 'site-navigation', 'homepage' ]查询设置;
  • use-homepage-actions.ts 在useMutationonSuccess回调中调用queryClient.invalidateQueries( { queryKey } )使对应缓存失效,随后onSuccess里再以{ exact: true }精确刷新——先让旧缓存失效、再按需重取,保证界面与服务器状态一致。

4.3 编辑器启动时注入客户端

editor/src/start.tsx 展示了应用引导流程:在编辑器启动入口调用createQueryClient()创建单例,并随QueryClientProvider注入根组件树,之后所有模块共享同一缓存实例。

五、工程化与发布配置

package.json 透露了该包的工程化细节:

  • 构建工具为tsupbuild脚本使用仓库根级的tsup.build.ts配置,产物同时输出dist/index.js(CJS)、dist/index.mjs(ESM)与dist/index.d.ts(类型声明);
  • exports字段按types/import/require三路分别映射——这正是 CHANGELOG 0.2.3 修复的内容,修复后 Node 与打包器在 ESM/CJS 混用场景下不再解析失败;
  • 当前版本号 4.4.0,包已发布到 npm(private: falsepublishConfig.access: "public"),许可为 GPL-3.0-or-later。

六、总结与升级建议

综合 CHANGELOG、源码与消费方代码,可以得到三个结论:

  1. @elementor/query是 Elementor 编辑器的服务端状态底座,设计哲学是“薄封装 + 单例客户端 + 保守默认策略”,业务复杂度由各模块自行通过 Hook 适配承担;
  2. 版本日志中的 0.2.0(分页)与 0.2.3(exports 修复)是两次实质性变更,其余版本均为依赖跟随;
  3. 升级该包时应重点回归分页面板、缓存失效链路以及 ESM 导入场景,同时避免使用__前缀的内部符号,以保证在依赖更新后代码仍可正常编译运行。

若要在自己的业务代码中复刻这套模式,建议沿用以[ '模块名', '资源名', 参数 ]组织queryKey、以onSuccess + invalidateQueries联动写读两侧、用staleTime控制缓存有效期的既有实践,这些范式在仓库的editor-site-navigation模块中均有现成实现可供参考。

【免费下载链接】elementorThe most advanced frontend drag & drop page builder. Create high-end, pixel perfect websites at record speeds. Any theme, any page, any design.项目地址: https://gitcode.com/GitHub_Trending/el/elementor

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

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

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

立即咨询