从 DataViews 到 DataForm:@wordpress/dataviews 的版本演进与 API 深度解析

原创2026-09-17 23:53:231,674 阅读
文章标签:后端前端

从 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 与校验 Hook useFormValidity。

这些导出可以从 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 通过共享的 useSelectionProps Hook 为 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 / option ARIA 角色;
  • 多选无需按住 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 的校验能力经历了三个阶段的演进:

  1. 起步:6.0.0 将 isValid 改为对象,内含 required 与 custom 两个规则;
  2. 扩展:11.2.0 为 number、integer、date、datetime 等字段补充 min/max、minLength/maxLength、pattern 等规则,14.1.0 为日期字段加入 min/max 范围校验;
  3. 受控化:10.0.0 引入 validity prop 与 useFormValidity Hook,移除了 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:config prop 隐藏视图配置的能力被回退,改由导出 DataViews.Footer 支持"极简 UI"。
  • 7.0.0:form.type 改为 form.layout.type;perPageSizes 移入新 config prop。
  • 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 等目录的源码为实现参照,即可快速掌握这套能力并跟上其持续迭代的节奏。

登录后查看全文
gutenberg