从 DataViews 到 DataForm:@wordpress/dataviews 的版本演进与 API 深度解析
从 DataViews 到 DataForm:@wordpress/dataviews 的版本演进与 API 深度解析
本文以
packages/dataviews/CHANGELOG.md为主干,系统梳理 Gutenberg 生态中@wordpress/dataviews包自 2023 年底首次发布以来的完整演进脉络——从最初的 DataViews 数据渲染组件,到 DataForm 表单编辑组件、DataViewsPicker 选择器组件,再到 Fields API 的字段类型、过滤操作符与校验体系的逐步成型。读者可以借此掌握该包当前(19.0.0)的组件构成、核心配置项、破坏性变更的迁移思路,以及这些能力在仓库源码中的落地方式。
一、包定位与版本演进全景
@wordpress/dataviews 是 Gutenberg 仓库中专门用于"列表数据渲染与编辑"的独立 npm 包,其定位在 packages/dataviews/package.json 中描述为:提供一套 API,用不同的布局(table、grid、list 等)渲染数据集。它对外暴露三个 React 组件和若干工具:
DataViews:用表格、网格、列表等多种布局渲染数据集,并内置搜索、过滤、排序、分页等交互;DataViewsPicker:面向"挑选/选择"场景优化的数据集渲染组件;DataForm:编辑数据集中的单个条目;- 工具函数
filterSortAndPaginate与校验 HookuseFormValidity。
这些导出可以从 packages/dataviews/src/index.ts 的入口文件逐一印证。
按 CHANGELOG 记录,该包从 0.2.0(2023-12-13) 一路迭代到 19.0.0(2026-09-10),期间版本号爬升极快,几乎每两周一个版本,且多次经历破坏性变更。整个演进可划分为几个清晰的阶段:
| 阶段 | 版本区间 | 主题 |
|---|---|---|
| 起步期 | 0.2.0 – 2.2.0 | DataViews 雏形,引入过滤操作符、分页工具函数 |
| 组件成形期 | 3.0.0 – 5.0.0 | 引入 DataForm、多布局、批量操作、自由组合模式 |
| 能力扩展期 | 6.0.0 – 10.0.0 | 校验体系对象化、DataViewsPicker、受控校验、布局家族扩张 |
| 布局深化期 | 11.0.0 – 14.0.0 | activity 布局、无限滚动重写、设计令牌迁移 |
| 稳定打磨期 | 15.0.0 – 19.0.0 | React 版本来回切换、richtext 控件引入又移除、time 字段类型、媒体比例控制 |
二、DataViews:核心渲染组件的演进
2.1 从单一视图到多布局家族
早期 DataViews 以列表布局起步,随后在 4.10.0 中将 title、media、description 三个字段从各布局各自的 view.layout.mediaField、view.layout.primaryField、view.layout.columnFields 中统一到 view 顶层:
const view = {
type: 'table',
titleField: 'title',
mediaField: 'media',
descriptionField: 'description',
fields: [ 'author', 'date' ],
};
到 19.0.0,src/constants.ts 中定义的布局常量已经形成完整家族:
- 常规布局:
table、grid、list、activity(活动时间线); - Picker 布局:
pickerTable、pickerGrid、pickerActivity。
11.0.0 引入 activity 布局,17.0.0 前的 picker 组件则从仅支持 pickerGrid/pickerTable 扩展到 pickerActivity。11.2.0 之前还通过 defaultLayouts 限制可用布局——该属性自 3.0.0 起取代了 supportedLayouts,接收以布局名为键、以默认 view 对象为值的对象,每次用户切换布局时都会应用。
2.2 view 对象:数据可见状态的事实来源
DataViews 是典型的受控组件:用户每次排序、过滤、切页、换布局,都会触发 onChangeView 回调,把新的 view 对象交还给消费方,由消费方负责据此查询数据源(如 WordPress REST API)。view 对象的核心字段(详见 packages/dataviews/README.md)包括:
type:布局类型;search:全局搜索关键字;filters:过滤数组,每项含field、operator、value与可选的isLocked(锁定过滤器,用户不可编辑,6.0.0 引入);page/perPage:页码与每页条数;startPosition:无限滚动时的起始位置(替代page);sort.field/sort.direction:排序字段与方向(asc/desc);titleField/mediaField/descriptionField与对应的showTitle/showMedia/showDescription;showLevels:是否展示层级,配合getItemLevel使用(4.11.0 引入层级能力);groupBy.field/groupBy.direction/groupBy.showLabel:分组配置(11.0.0 由groupByField更名而来);infiniteScrollEnabled:是否启用无限滚动;fields:当前可见的字段 id 及顺序;layout:布局特有配置。
分组功能本身也有演进史:5.0.0 先在 grid 布局支持 groupByField,6.0.0 扩展到 table 布局,9.0.0 覆盖 list 布局,11.0.0 改名 groupBy.field 并支持方向与标签显示控制。
2.3 分页与无限滚动
分页是 DataViews 的标配能力。perPageSizes 用于控制每页可选条数(默认 [10, 20, 50, 100],且需 2~6 个元素,否则不渲染该控件);11.3.0 还在页脚增加了总数统计。
无限滚动的实现经历过一次重要重写:7.0.0 先通过 paginationInfo 传入 infiniteScrollHandler 实现各布局的无限滚动;14.0.0 则改用 IntersectionObserver 卸载不可见条目以提升性能,并将启用方式简化为仅需 infiniteScrollEnabled 与 startPosition 两个 view 属性。这一点可以在 src/utils/filter-sort-and-paginate.ts 的第 128-144 行看到两种分页分支:启用无限滚动时按 startPosition 切片,否则按 page/perPage 做传统分页。
2.4 操作(Actions API)与批量选择
DataViews 通过 Actions API 提供行内操作与批量操作,动作对象包含 id、label(可接受函数以根据选中项动态生成文案,2.0.0 起支持)、isPrimary、icon、isEligible、supportsBulk、disabled、context、callback、RenderModal(弹窗式操作,10.0.0 起可通过 modalHeader、modalSize、modalFocusOnMount 等定制)等属性。callback 自 2.0.0 起可接收 registry 参数。
围绕批量操作,多选交互逐步完善:
- 5.0.0 在表格布局固定操作列;
- 6.0.0 支持 Ctrl/Cmd+点击多选行;
- 17.3.0 通过共享的
useSelectionPropsHook 为 table、grid 布局带来 Shift+Click 范围选择,并扩展到picker-table、picker-grid、picker-activity; - 10.0.0 移除了
isDestructive属性,破坏性操作改由弹窗确认流程表达。
选择状态由 selection 与 onChangeSelection 控制(4.0.0 从 onSelectionChange 更名而来,参数改为 id 列表),传入时组件表现为受控组件。
2.5 自由组合(Free Composition)模式
5.0.0 起 DataViews 支持自由组合:导出 DataViews.Search、DataViews.FiltersToggle、DataViews.Filters、DataViews.Layout、DataViews.Pagination、DataViews.BulkActionToolbar 等子组件,消费方可自行编排界面。9.1.0 增加了 DataViews.FiltersToggled,17.0.0 则拆分了 DataViews.BulkActionToolbar(仅渲染批量信息与操作按钮)与 DataViews.Footer(含分页的完整页脚)。组件依据是否传入 children 自动在受控模式与自由组合模式间切换。
2.6 媒体预览:从固定方形到可配置比例
媒体预览是 grid/table 布局的重头戏。18.0.0 引入 aspectRatio 布局选项,允许从一组预设比例中选择媒体预览的宽高比,默认 1/1;18.1.0 又加入 mediaFit 选项,支持 cover(裁剪填满)与 contain(完整容纳并留白)两种填充方式,并可通过 config.mediaFitControl 向用户暴露"原始宽高比"切换。仓库 src/constants.ts 第 58-66 行的 MEDIA_ASPECT_RATIOS 常量定义了全部 7 种预设比例(1/1、4/3、3/4、3/2、2/3、16/9、9/16)。
三、DataViewsPicker:面向选择的专用组件
9.1.0 引入的 DataViewsPicker 与 DataViews 最大的区别在于交互模型:
- 列表容器使用
listbox/optionARIA 角色; - 多选无需按住 Ctrl/Cmd,整行点击即可切换选中状态;
- 条目本身不渲染操作,所有操作以文字按钮形式集中在页脚(13.0.0 起支持在 view 配置下拉中提供
onReset重置功能,12.0.0 的 DataViews 侧同样加入该能力); - 翻页后仍保持跨页选择状态。
实现约束上,picker 仅支持 pickerGrid 与 pickerTable 布局(16.0.0 增加 pickerActivity),必须作为受控组件使用(selection 与 onChangeSelection 必填),且不支持 isItemClickable、renderItemLink、onClickItem、getItemLevel、header。多选时所有操作需声明 supportsBulk: true,单选则用 supportsBulk: false;混用时回退为单选。17.0.0 起 DataViewsPicker.BulkActionToolbar 与 DataViews.BulkActionToolbar 行为对齐。
四、DataForm:从单布局到五布局家族
4.1 布局演进
DataForm 于 3.0.0 首次出现,4.0.0 引入多布局支持与 panel 布局,此后布局家族不断扩张:
- 7.0.0:新增
card布局,表单类型从form.type迁移为form.layout.type; - 9.0.0:新增
row布局(横向单行排列,支持alignment与按字段设置flex样式); - 10.3.0:新增
details布局(基于原生<details>元素的可折叠区块); - 14.0.0:card 布局改用
@wordpress/ui的Card/CollapsibleCard,视觉样式随之调整。
panel 布局是最有特点的一种——它以折叠面板/下拉弹层承载字段编辑,配置项包括:
labelPosition:side、top或none(默认side);editVisibility:always或on-hover;openAs:dropdown或modal(7.0.0 起支持 modal);summary:指定面板头部展示的汇总字段;showPlaceholderIfEmpty(19.0.0 新增):值为空时在摘要中显示字段placeholder。
4.2 组合字段与 19.0.0 的语义收紧
DataForm 支持通过 children 组合字段。19.0.0 对组合字段做了一次语义收紧:组合字段被纯化为布局容器——它的 id 不再与字段定义解析,共享该 id 的字段不再向分组贡献校验规则,panel 布局也不再用它作为折叠摘要或只读状态依据,转而回退到分组第一个叶子字段。CHANGELOG 给出了标准迁移示例:
// 之前:分组因共享 id 而使用 discussion 字段作为摘要
const form = {
layout: { type: 'panel' },
fields: [
{ id: 'discussion', children: [ 'comment_status', 'ping_status' ] },
],
};
// 之后:显式声明摘要字段,否则将由第一个子字段 comment_status 汇总
const form = {
layout: { type: 'panel' },
fields: [
{
id: 'discussion',
layout: { type: 'panel', summary: 'discussion' },
children: [ 'comment_status', 'ping_status' ],
},
],
};
如果此前依赖同名字段的 isValid 规则作用到分组上,则需把这些规则移到子字段上。这一变更在 src/hooks/use-form-validity.ts 第 66-84 行的 processFormField 中有明确的实现注释:组合字段是布局容器,其 id 永远不会被解析为字段定义,只对子字段做校验。
4.3 校验体系:从隐式到受控
DataForm 的校验能力经历了三个阶段的演进:
- 起步:6.0.0 将
isValid改为对象,内含required与custom两个规则; - 扩展:11.2.0 为
number、integer、date、datetime等字段补充min/max、minLength/maxLength、pattern等规则,14.1.0 为日期字段加入min/max范围校验; - 受控化:10.0.0 引入
validityprop 与useFormValidityHook,移除了isItemValid,使校验状态完全由消费方掌控。
validity 对象以字段 id 为顶层键,每个字段可声明 required、elements、pattern、minLength、maxLength、min、max、custom 规则的校验结果。每条规则包含 type(validating / invalid / valid)与可选 message——其中 valid 状态用于表达"曾经无效、现已有效"的过渡,useFormValidity 仅为异步 custom 校验实现此语义。组合字段通过 children 属性嵌套子字段校验结果。useFormValidity 的实现(src/hooks/use-form-validity.ts)用 fastDeepEqual 对比前后快照跳过未变更字段,并通过计数器令牌避免过期的异步校验结果回写。
几个值得注意的校验细节:
- 9.0.0 起
elements校验独立于custom校验,可分别开关; - 9.1.0 支持 array 字段的 elements 校验(异步);
- 18.0.0 起,通过
isVisible隐藏的字段跳过校验,隐藏期间required等规则不再让表单失效; array字段类型的内置校验在 18.0.0 起将空值视为合法,改用required规则强制非空(声明了elements的字段仍拒绝空值)。
4.4 表单控件库的扩张与收缩
DataForm 的 Edit 控件从最初的几个一路扩充,形成了一套相当完整的输入控件清单:text、textarea、email、telephone、url、password、integer、number、date、datetime、time、color、checkbox、radio、toggle、toggleGroup、select、combobox、adaptiveSelect、array。其中:
- 9.0.0 移除了
boolean控件,改用checkbox/toggle; - 11.3.0 新增
combobox,12.0.0 新增adaptiveSelect; - 17.2.0 短暂加入
richtext富文本控件(装配@wordpress/rich-text),但因在打包包内组装@wordpress/rich-text会破坏从 npm 安装的插件,18.0.0 将其移入@wordpress/editor,19.0.0 进一步删除相关config选项并移除privateApis导出——需要富文本字段时改由自定义Edit组件实现。
日期/时间控件在 18.1.0 修复了时区错位与日历高亮问题(UTC 偏移站点、浏览器时区兜底等),并在 19.0.0 增加了"站点时区提示"文案——当站点时区与访客不同时,控件下方会标注站点时区名称(如 (CEST) Europe/Madrid)或 UTC 偏移。18.1.0 还让日历月份与周名称跟随站点语言、RTL 后台获得 RTL 日历。
五、Fields API:字段描述能力的深度展开
5.1 字段类型与默认实现
字段通过 type 声明类型,即可获得默认的排序、渲染、编辑、校验实现。19.0.0 支持的字段类型包括 text、integer、number、datetime、date、time、media、boolean、email、password、telephone、color、url、array。类型体系也经过演进:0.9.0 移除 enumeration 类型,11.0.0 将 FieldType 更名为 FieldTypeName。
其中 time 是 18.0.0 新增的类型,值采用无时区的墙钟时间(RFC 3339 partial-time,HH:mm 或 HH:mm:ss),不随访客时区变化。其实现(src/field-types/time.tsx)颇有巧思:因为时间不携带日期,格式化时必须锚定到一个固定日期(2000-01-01,见第 28 行注释),且解析与渲染都在站点时区进行,两次转换相互抵消,保证墙钟时间在任意访客时区下不变;同时选择固定日期而非"今天",避免夏令时切换造成偏移。time 的排序对无法解析的值统一排到末尾(第 63-75 行)。
5.2 过滤操作符体系
过滤是 DataViews 的核心交互,操作符体系也历经多轮迭代:
- 0.8.0 引入
isAll/isNotAll(AND 语义)、is/isNot(单选)、isAny/isNone(多选),并将旧操作符in/notIn标记废弃; - 2.0.0 正式移除
in/notIn; - 5.0.0 大规模扩充操作符,加入
lessThan、greaterThan、lessThanOrEqual、greaterThanOrEqual、contains、notContains、startsWith、between、on、notOn、before、after、inThePast、over、beforeInc、afterInc; - 11.2.0 弃用
isNotAll(仍保留,映射为isNone并给出弃用警告,见 src/utils/filter-sort-and-paginate.ts 第 64-71 行); - 18.0.0 将
on、notOn、before、after、beforeInc、afterInc、between从日期推广到时间值; - 19.0.0 支持
isAny/isNone作用于数值字段,并从number、integer、text、email、url、telephone的有效操作符中剔除isAll(标量值不存在"包含所有"语义)。
各字段类型拥有默认的有效操作符集合,并可通过 filterBy.operators 覆盖;filterBy: false 可让字段完全退出过滤(5.0.0 起支持),filterBy.isPrimary 可将过滤器设为"主过滤器"(6.0.0 引入的锁定过滤器 isLocked 与之配套)。仓库 src/constants.ts 第 6-27 行集中定义了全部操作符常量。单选与多选操作符不可混用:只要出现单选操作符,多选操作符即被丢弃。
5.3 取值、赋值与格式化
字段通过 getValue / setValue 读写数据:两者均可自动生成,字段 id 被当作点号路径处理(如 user.profile.name 会自动生成读取 item.user.profile.name 的 getValue 与写入嵌套结构的 setValue),需要存储格式与展示格式相互转换时也可自定义实现(README 给出了 boolean 转字符串选项的完整示例)。setValue 在 9.0.0 引入,用于修复嵌套数据下的过滤器与 panel 布局(modal)编辑。
getValueFormatted(11.0.0 起随 format prop 引入)负责展示层格式化,各类型提供默认实现:date/datetime/time 使用 PHP 日期格式字符串并支持 weekStartsOn,number 支持千分位/小数分隔符与 decimals,integer 支持千分位。format 配置同时作用于 render、Edit 控件与过滤控件。例如 time 字段的 Edit 控件是否提供秒输入,取决于格式字符串是否包含秒 token;而 time 字段若误用日期 token(如 'F j, Y g:i a'),会渲染内部锚定日期(January 1, 2000 ...),因此 README 明确建议:需要日期的字段请用 datetime。
5.4 元素、惰性加载与渲染
elements 提供字段的可选值列表(含 value、label、description),过滤器会将其作为预设选项。9.1.0 起可用 getElements 异步惰性加载元素,但该方法可能在组件生命周期中被频繁调用(如每行记录的 render 都会触发),消费方必须自行缓存结果。10.0.0 支持异步加载元素;渲染自定义通过 render 属性实现,可访问 item、field 与可选的 config(目前唯一属性是媒体字段的 sizes,6.0.0 引入用于响应式图片)。
5.5 可见性与状态控制
字段级 API 还提供了精细的展示控制:
enableSorting/enableHiding/enableGlobalSearch:分别控制排序、隐藏、全局搜索参与度(默认true/true/false);readOnly(5.0.0 起)与isDisabled(14.1.0 起):前者用 render 替代 Edit 渲染,后者仍渲染 Edit 控件但处于禁用态,两者区别在 19.0.0 的文档中得到明确澄清,且 DataForm 在 19.0.0 修复了只读字段无需 Edit 控件即可渲染的问题;isVisible(17.3.0 前已存在):按条目动态控制字段可见性,隐藏字段跳过校验。
六、工程化演进:模块、依赖与设计体系
6.1 模块格式与 React 版本
- 2.0.0 将
process.env.IS_GUTENBERG_PLUGIN替换为globalThis.IS_GUTENBERG_PLUGIN,并将最低 Node.js 版本提升至 v18.12.0; - 4.11.0 修复 CommonJS 导出;11.2.0 完成符合规范的 CJS/ESM 双模块改造;
- React 版本经历两次反转:15.0.0 升级到 React 19,16.0.0 又回退到 React 18,17.2.0 将 peer 依赖放宽为
^18 || ^19以同时支持两个大版本; - 17.0.0 将
@types/react调整为可选 peer 依赖,避免与消费方 React 类型版本冲突。
6.2 设计令牌与 UI 组件迁移
自 15.0.0 起,DataViews 持续将样式从 @wordpress/base-styles 的 SCSS 变量迁移到 @wordpress/theme 的 CSS 自定义属性(设计令牌),期间修复了大量令牌迁移导致的间距漂移(表头内边距 32px→16px、头部按钮间隙 8px→4px、网格间距 32px→24px 等)。12.0.0 有一个对插件作者重要的破坏性变更:@wordpress/theme/design-tokens.css 不再嵌入 DataViews 样式表,WordPress 之外的集成方必须显式引入设计令牌样式表。
组件层面则持续从 @wordpress/components 的私有 API 迁移到公开组件:Tooltip、Badge、Card/CollapsibleCard、Calendar/RangeCalendar、ColorPicker、Menu 等先后改用 @wordpress/ui 的公开实现,并"内部化"了一批 Validated* 控件(18.0.0-18.1.0 期间密集进行),最终在 18.1.0 移除了对 @wordpress/private-apis 的全部依赖。依赖上还经历了 classnames → clsx(1.2.0)、@ariakit/react 的持续升级(0.4.7 → 0.4.39)与 @base-ui/react 1.8.0 的引入(19.0.0,服务于 /wp 入口的捆绑 UI 组件)。
6.3 测试与文档建设
测试基建随版本逐步现代化:更新 Testing Library(12.0.0)、Jest 类型定义至 v30(17.3.0)、渲染测试迁移到 Vitest Browser Mode(19.0.0)、升级 @types/node 至 v24(19.0.0)。文档方面,17.1.0 为 DataViews、DataViewsPicker、DataForm 补充组件文档,16.0.0 将 DataViews 组件纳入 Design System MCP Server 的文档组件清单,11.0.0 起持续完善 type、layout、readOnly、description、placeholder、操作符等属性的文档,19.0.0 则导出 DataViewsProps 与 ItemWithId 类型并逐属性注释。
七、版本迁移速查:主要破坏性变更清单
对使用该包的开发者,以下破坏性变更最值得关注(按时间倒序):
- 19.0.0:组合字段纯化为布局容器,面板摘要与校验规则改用
layout.summary与子字段承载;移除richtext相关config选项。 - 18.0.0:内置
richtext控件与privateApis导出移除,富文本改用自定义Edit。 - 17.0.0:
DataViewsPicker.BulkActionToolbar不再含分页,完整页脚改由DataViewsPicker.Footer提供。 - 12.0.0:WordPress 之外的集成需显式引入设计令牌样式表。
- 10.0.0:校验改为受控(
validity+useFormValidity);summary从字段级移到 layout 级;select控件不再注入空选项;移除isDestructive。 - 9.0.0:移除
boolean控件(改用checkbox/toggle);自定义empty元素不再包裹<p>。 - 8.0.0:
configprop 隐藏视图配置的能力被回退,改由导出DataViews.Footer支持"极简 UI"。 - 7.0.0:
form.type改为form.layout.type;perPageSizes移入新configprop。 - 6.0.0:
isValid对象化(required+custom),字段默认不再是必填。 - 5.0.0:声明
Edit或type的字段默认成为过滤器;onClickItem替换为renderItemLink。 - 4.0.0:
onSelectionChange→onChangeSelection;字段header属性 →label;DataForm 的visibleFields→fields。 - 3.0.0:
hiddenFields→fields(可见字段数组);supportedLayouts→defaultLayouts。 - 2.0.0:移除
in/notIn操作符;Node 最低版本提升至 v18.12.0。
八、总结
@wordpress/dataviews 从 2023 年末的 0.2.0 一路演进到 2026 年的 19.0.0,完成了从"单一列表渲染组件"到"数据渲染 + 选择 + 编辑"三大组件家族的蜕变。其核心设计——以受控 view 对象驱动渲染、以 Fields API 统一字段行为、以 validity 受控化校验——构成了数据密集后台界面(尤其是 WordPress 后台的站点、页面、模板、模式等管理页)的基础设施。对希望深度使用的开发者,建议以 packages/dataviews/README.md 为操作手册、以本 CHANGELOG 为演进地图、以 src/field-types、src/components/dataform-layouts 等目录的源码为实现参照,即可快速掌握这套能力并跟上其持续迭代的节奏。