资讯动态

从 @zag-js/switch 变更日志解读 Zag Switch 状态机的 249 个版本演进

发布时间:2026/10/14 15:29:59 来源:尧图企业网站定制
前端UI组件【免费下载链接】zagBuild your design system in React, Solid, Vue, Svelte or Vanilla. Powered by finite state machines项目地址https://gitcode.com/gh_mirrors/za/zag点击查看免费下载本文以 Zag 仓库中 packages/machines/switch/CHANGELOG.md 为主体完整梳理zag-js/switch从 0.1.0 到 1.46.0 的版本演进脉络哪些版本是依赖联动更新、哪些版本引入了readOnly、splitProps、data-*属性等真正的功能与修复。并结合 switch.machine.ts、switch.connect.ts 等源码与 e2e 测试帮助你在升级依赖时判断要不要升、升到哪并理解每条变更记录背后对应的状态机实现。一、包的定位与变更日志概览zag-js/switch是 Zag基于有限状态机的跨框架组件逻辑库中 Switch 开关组件的核心逻辑包其 package.json 描述为 Core logic for the switch widget implemented as a state machine。它不含任何 React/Vue 等框架代码框架接入由zag-js/react、zag-js/vue等独立包完成。当前仓库中该包版本为1.46.0变更日志共记录了249 个版本条目从 0.1.0 到 1.46.0统计下来有以下规律约 240 个条目的主体是Updated dependencies工作区联动升级即本包自身没有变更仅因依赖的zag-js/core、zag-js/types、zag-js/dom-query、zag-js/anatomy、zag-js/utils、zag-js/focus-visible等包发版而跟着升版其余约 30 个条目带有真实的变更说明含贡献者 commit 引用这些才是真正影响行为的版本遵循 semverMinor Changes小节对应次版本号0.x.0升级可能包含破坏性变更Patch Changes对应修复或补丁。从当前 package.json 的依赖列表可以印证依赖的瘦身史早期版本还依赖zag-js/form-utils、zag-js/dom-event、zag-js/visually-hidden如今已收敛为 6 个核心依赖anatomy、core、dom-query、focus-visible、types、utils这正是变更日志 0.81.0 Refactor and streamline packages 一类的结果。二、0.1.0 → 0.10.x机器初建与受控模式定型0.1.0新增 switch machine日志首条PR #557Add new switch machine。这是该包在 Zag 仓库中的起点zag-js/core0.2.11作为基础依赖引入。0.5.0移除 defaultChecked改为可受控的 checked破坏性日志原文Remove thedefaultCheckedproperty in favor of thecheckedproperty that can now be controlled。原示例代码展示了通过useMachine的 context 让外部值驱动组件// 初始即为选中态 const [state, send] useMachine(machine, { id: 1, checked: true }) // 当 context.checked 变化时组件同步更新 const [state, send] useMachine(machine, { id: 1 }, { context: { checked: true }, })在今天的源码中这一设计依然成立switch.machine.ts 中checked是一个bindable字段同时支持value: prop(checked)受控、defaultValue: prop(defaultChecked)非受控初始值并在值变化时回调onCheckedChange。注意 1.x 中defaultChecked作为初始值又回归了见下文0.5.0 移除的是旧版不可控的 defaultChecked 语义。0.8.0 → 0.10.x受控模式 bug 与事件修复0.8.0PR #629Refactor switch machine to fix transition bugs in controlled mode —— 重构机器以修复受控模式下的状态迁移 bug0.9.0两条变更Remove unused attributes and make it consistent with checkbox与 Checkbox 的属性集对齐与 Refactor machines to wire out state and transitions correctly正确对外暴露状态与迁移0.10.0修复 Checkbox and Switch emitsonClicktwice when parent element is clicked点击父元素时 onClick 触发两次与 setCheckeddoesnt work。这个点击两次触发问题对应的正是 switch.connect.ts 中getRootProps的onClick实现当点击目标就是隐藏 input 时执行event.stopPropagation()避免父级 label 的合成点击与原生 checkbox 点击叠加0.10.3 / 0.11.0构建工具链变更Use vite to build packages → Rebuild all packages using tsup对应 package.json 中tsup构建脚本与dist发布产物。三、0.12.0 → 0.20.xAPI 语义的几次关键转折0.12.0setChecked 修复PR #741Fix issue whereapi.setCheckeddoes not work for Checkbox and Switch components。程序化设置 checked 的能力api.setChecked/api.toggleChecked在此前后才真正稳定源码中对应 switch.connect.tssetChecked(checked) { send({ type: CHECKED.SET, checked, isTrusted: false }) }, toggleChecked() { send({ type: CHECKED.TOGGLE, checked: checked, isTrusted: false }) },isTrusted: false表示这是非用户真实交互发起的事件机器据此决定是否补发 change 事件见下文事件路由。0.14.0放弃 dom.queryByIdRefactor machines to avoid the use ofdom.queryById, this causes the machine to throw in React when the key of an element is reassigned。这是 React 场景下常见的元素 key 重挂载后机器拿不到 DOM问题的修复源码中可见其延续switch.dom.ts 通过scope.getById框架注入的 id 注册表而非全局document.querySelector查找元素。0.17.0 / 0.18.0命名规范与解剖结构调整0.17.0PR #789Refactor machine event handling and renamePublicApitoApi。类型Api即如今 switch.types.ts 中的SwitchApiindex.ts中再导出为Api。同版本还有一个 PR #807 修复Re-enable the input after removingdisabledattribute from the parent fieldset —— 父级 fieldset 解除 disabled 后 input 恢复可用由 switch.machine.ts 的trackFormControlStateeffect 监听 fieldset 变化并同步fieldsetDisabled上下文0.18.0破坏性Remove input from anatomy。解剖结构从此固定为root / label / control / thumb四部分见 switch.anatomy.tsexport const anatomy createAnatomy(switch).parts(root, label, control, thumb)隐藏input typecheckbox不再算作解剖部件而是 root 内部的实现细节由getHiddenInputProps()单独下发。0.19.x / 0.20.0change 事件与回调命名统一0.19.0Checkbox, Switch: Dispatch change event when checked state is set programmatically to get it working in Solid.js form libraries —— 程序化设置也派发原生 change 事件保证 Solid 表单库能感知。源码中即 switch.machine.ts 的dispatchChangeEvent用queueMicrotask在隐藏 input 上派发0.19.1修复 change callbacks could be executed excessively when no value changed0.20.0破坏性BREAKING: Unify all callbacks to follow a consistent naming convention —— 这就是onCheckedChange命名定型的版本与 switch.types.ts 中onCheckedChange?: ((details: CheckedChangeDetails) void) | undefined一致细节对象为{ checked: boolean }。四、0.21.0 → 0.71.0data 属性、readOnly 与工程化完善这一区间的条目大多为依赖联动值得逐条记住的功能/修复版本如下版本类型变更说明0.26.0Patch修复dir属性未应用到所有部件的问题RTL 支持0.29.0MinorExpose more internal for better DX when building component libraries0.37.1Patch暴露splitProps函数改进在 Zag 之上自研组件的 DX0.46.0Patch修复指针在元素外释放时data-active不被移除的问题0.48.0Minor新增readOnlyprop 支持PR #14350.58.0Minor统一invalid、required、readOnly等表单属性行为导出Service供 Lit 类组件使用0.69.0Minor改进 focus visible 支持Switch 等组件在键盘操作时给对应元素加data-focus-visible0.71.0MinorWe no longer shipsrcfiles in the packages发包产物瘦身splitProps在源码中的对应物是 switch.props.tsexport const splitProps createSplitPropsPartialSwitchProps(props)其中props用createPropsSwitchProps()声明了机器认识的完整 prop 白名单checked、defaultChecked、dir、disabled、form、getRootNode、id、ids、invalid、label、name、onCheckedChange、readOnly、required、value把其余属性留给 UI 层透传。readOnly的行为在 switch.connect.ts 中体现为隐藏 input 的onClick中若readOnly为真则event.preventDefault()并输出data-readonly属性。data-active的 0.46.0 修复对应 switch.machine.ts 的trackPressEventeffectonPressStart置active: trueonPress与onPressEnd均置回false确保按下后指针移出元素再松开也能正确清除按下态。五、0.81.0 → 1.46.01.0 里程碑与稳定性收尾1.0 前后的重要条目0.81.0Refactor and streamline packages —— 工作区包结构整理form-utils、dom-event、visually-hidden等在此前后并入现有包与当前 package.json 的 6 依赖清单相互印证1.0.1Exposecomponent.Machinetype to help when typecasting generic components like combobox and select附了 combobox 的类型断言示例useMachine(combobox.machine as combobox.MachineItem, ...)。对应 switch.types.ts 中的SwitchMachine MachineSwitchSchema导出1.3.1Improve focus behavior in Safari browser —— 源码中可见其落地switch.connect.ts 的getRootProps().onClick里if (isSafari()) { dom.getHiddenInputEl(scope)?.focus() }即点击根元素时在 Safari 中手动把焦点移给隐藏 input1.18.5Fix issue destructuring returned api could throw an ESLintunbound-methodwarning —— 针对框架接入层解构api时 ESLint 误报的修复1.23.0PR #2673Adddata-requiredto label parts1.25.0PR #2734Fix issue wheredata-activeis inconsistently applied whendisabledstate changes at runtime —— 运行时切换disabled时data-active表现不一致的修复对应 switch.machine.ts 中 watchprop(disabled)变化后执行removeFocusIfNeeded1.27.1PR #2822Export missing types1.33.0Fix issue whereapi.toggleChecked()doesnt work as expected1.43.3类型写法调整 —— 把可选属性写成显式的?: T | undefined而不再用zag-js/types的Partial包装该包装遮蔽了内置PartialT曾导致vue/compiler-sfc在interface X extends PartialY上崩溃。如今 switch.types.ts 中的ElementIds等类型即为普通 interface。data 属性全景变更记录的最终形态把上述data-*相关变更0.46.0、0.58.0、0.69.0、1.23.0、1.25.0汇总可以得到当前 switch.connect.ts 输出的完整状态属性集这也是你用 CSS 定制开关样式时的钩子const dataAttrs { data-active: dataAttr(active), // 按下态 data-focus: dataAttr(focused), // 聚焦态 data-focus-visible: dataAttr(focusVisible), // 键盘可见聚焦 data-readonly: dataAttr(readOnly), // 只读 data-hover: dataAttr(context.get(hovered)), // 悬停 data-disabled: dataAttr(disabled), // 禁用 data-state: checked ? checked : unchecked, // 选中态 data-invalid: dataAttr(prop(invalid)), // 无效 data-required: dataAttr(required), // 必填1.23.0 加入 label 部件 }data-state采用值checked/unchecked而非空串是 Zag 系列组件统一的约定便于写[data-statechecked] { ... }这样的样式选择器。e2e 断言也直接依赖这些属性例如 switch.model.ts 中通过[data-part]定位并断言data-focus、data-disabled。六、结合源码理解事件流一次点击发生了什么变更日志中 0.10.0、0.19.x、1.33.0、1.43.3 等条目反复围绕事件是否正确触发理解 switch.machine.ts 的事件表就能把这些修复串起来on: { CHECKED.TOGGLE: [ { guard: not(isTrusted), actions: [toggleChecked, dispatchChangeEvent] }, { actions: [toggleChecked] }, ], CHECKED.SET: [ { guard: not(isTrusted), actions: [setChecked, dispatchChangeEvent] }, { actions: [setChecked] }, ], CONTEXT.SET: { actions: [setContext] }, },规则是真实用户交互event.isTrusted true来自隐藏 input 的 click走第二条迁移只改状态因为原生 change 事件浏览器自己会派发程序化调用api.setChecked/api.toggleCheckedisTrusted: false走第一条在改状态后主动dispatchChangeEvent补发原生 change 事件——这正是 0.19.0 为 Solid 表单库做的行为也是 1.33.0 之前toggleChecked行为异常的修复落点。状态写入后的联动由watch完成switch.machine.tschecked变化触发syncInputElement同步隐藏 input 的 DOM checkeddisabled变化触发removeFocusIfNeeded。表单联动则交给trackFormControlStateeffect监听 fieldset 的 disabled写入fieldsetDisabled上下文与prop(disabled)一起构成计算属性isDisabled与 form 的 reset 事件reset 时把 checked 恢复为初始值context.initial(checked)。七、可验证的行为E2E 测试与示例页面仓库为 switch 提供了 Playwright E2E 用例 e2e/switch.e2e.ts可直接作为变更日志中各项修复的验收清单无障碍检查通过checkAccessibility点击后 checkbox 被选中Tab 键可达焦点聚焦时按空格切换选中对应trackPressEvent中isValidKey: (event) event.key disabled时输入被禁用且data-disabled出现、不可聚焦点击 label 不会导致 input 失焦expectBlurCount(0)。测试模型 e2e/models/switch.model.ts 的 selector 同时印证了 anatomy 的四部件结构[data-scopeswitch][data-partroot | label | control]隐藏 input 则挂在 root 内。可运行的参考实现见 examples/next-ts/pages/switch/basic.tsx其中展示了最典型的接线方式const service useMachine(zagSwitch.machine, { id: useId(), name: switch, ...controls.context }) const api zagSwitch.connect(service, normalizeProps) label {...api.getRootProps()} input {...api.getHiddenInputProps()} / span {...api.getControlProps()} span {...api.getThumbProps()} / /span span {...api.getLabelProps()}{api.checked ? enabled : disabled}/span /label演示页还附带了api.toggleChecked()按钮用于手工验证程序化切换1.33.0 修复的场景。八、安装与使用结合变更日志版本能力按 README 与官网文档 website/data/components/switch.mdx安装机器包与框架包npm install zag-js/switch zag-js/react # or yarn add zag-js/switch zag-js/react最小用法与 usage 文档片段 一致import * as zagSwitch from zag-js/switch import { useMachine, normalizeProps } from zag-js/react import { useId } from react function Switch() { const service useMachine(zagSwitch.machine, { id: useId() }) const api zagSwitch.connect(service, normalizeProps) return ( label {...api.getRootProps()} input {...api.getHiddenInputProps()} / span {...api.getControlProps()} span {...api.getThumbProps()} / /span span {...api.getLabelProps()}{api.checked ? On : Off}/span /label ) }关键 props 均可在 switch.types.ts 中查到类型定义Prop说明checked/defaultChecked受控选中态 / 初始选中态受控优先onCheckedChange选中变化回调细节为{ checked }disabled/readOnly/required/invalid禁用 / 只读 / 必填 / 无效分别映射data-disabled等属性name/value/form表单提交相关value默认on见 switch.machine.ts 的 props 默认值label本地化标签默认switchids为各部件指定 id便于组合dirltr/rtl方向示例站的可玩控制项disabled、readOnly两个布尔开关定义在 shared/src/controls.ts。九、面向升级的实用结论基于这份变更日志可以给出明确的升级策略依赖联动版本约 240 个如 1.46.0、1.45.0、1.35.x 这类仅含Updated dependencies的条目升级它们是跟随zag-js/core等基础包的安全动作无独立行为变更破坏性版本集中在 0.x0.5.0checked 语义、0.17.0PublicApi→Api、0.18.0anatomy 移除 input、0.20.0回调命名统一都在 1.0 之前如今使用 1.46.0 不受影响关注行为修复的版本1.25.0运行时 disabled 与data-active、1.33.0toggleChecked、1.43.3类型导出方式Vue SFC 场景——如果你的应用依赖程序化切换、运行时禁用或 Vue 编译链跨这些版本升级时值得回归验证适用前提本文结论以当前仓库1.46.0源码为准zag-js/switch本身与框架无关文中接线示例以zag-js/react为示例其他框架接入包的 API 形态相同。十、小结zag-js/switch的变更日志表面上是 249 个版本条目的流水账实际讲述了一个状态机组件的成熟史0.1.0 建机 → 0.5.0/0.8.0 定型受控模式 → 0.17.0~0.20.0 统一 API 与事件语义 → 0.48.0/0.58.0/1.23.0 补全表单与 data 属性 → 1.x 阶段做 Safari、类型导出等长尾修复。每条日志都能在 packages/machines/switch/src 的六个源文件machine、connect、anatomy、dom、props、types中找到对应实现配合 e2e/switch.e2e.ts 的自动化断言可以形成日志条目 → 源码实现 → 测试验证的完整证据链这也是阅读 Zag 任意机器包 CHANGELOG 的通用方法。赞分享前端UI组件【免费下载链接】zagBuild your design system in React, Solid, Vue, Svelte or Vanilla. Powered by finite state machines项目地址https://gitcode.com/gh_mirrors/za/zag点击查看免费下载相关推荐zag Scroll Area 状态机从 CHANGELOG 版本演进看 zag-js/scroll-area 的完整实现原理zag Scroll Area 状态机从 CHANGELOG 版本演进看 zag js/scroll area 的完整实现原理 在 zag zag js前端UI组件zag-js/menu 状态机深度解读从 CHANGELOG 看 Zag 菜单组件的演进与实现原理zag js/menu 状态机深度解读从 CHANGELOG 看 Zag 菜单组件的演进与实现原理 zag js/menu 是 Zag 生态中基于有限状态前端UI组件zag 中 zag-js/select 的版本演进全解状态机 Select 组件从 0.1.0 到 1.46.0 的 API 变迁与实现要点zag 中 zag js/select 的版本演进全解状态机 Select 组件从 0.1.0 到 1.46.0 的 API 变迁与实现要点 本文基于 pa前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑