Chakra UI/Zag 项目入门指南:跨框架UI状态管理
2025-07-08 06:29:54作者:羿妍玫Ivan
什么是Zag?
Zag是一个基于状态机的UI组件库,它通过将交互逻辑抽象为状态图表(statechart)的方式,为开发者提供可复用的组件交互模式。该项目最大的特点是"一次编写,多框架运行",目前支持React、Vue 3、Solid.js和Svelte等主流前端框架。
核心优势
- 跨框架兼容:同一套交互逻辑可在不同框架中使用
- 状态机驱动:清晰的组件状态管理
- 无障碍支持:内置可访问性最佳实践
- 类型安全:完整的TypeScript支持
安装步骤
1. 安装组件机器
每个UI组件都有对应的"机器"(machine),这是Zag的核心概念。以工具提示(tooltip)组件为例:
npm install @zag-js/tooltip
# 或使用yarn
yarn add @zag-js/tooltip
2. 安装框架适配器
根据你使用的前端框架,安装对应的适配器:
# React项目
npm install @zag-js/react
# Vue 3项目
npm install @zag-js/vue
# Solid.js项目
npm install @zag-js/solid
# Svelte项目
npm install @zag-js/svelte
使用示例
React中使用工具提示
import * as tooltip from "@zag-js/tooltip"
import { useMachine, normalizeProps } from "@zag-js/react"
function Tooltip() {
const service = useMachine(tooltip.machine, { id: "1" })
const api = tooltip.connect(service, normalizeProps)
return (
<>
<button {...api.getTriggerProps()}>悬停我</button>
{api.open && (
<div {...api.getPositionerProps()}>
<div {...api.getContentProps()}>提示内容</div>
</div>
)}
</>
)
}
Vue 3中使用(JSX方式)
import * as tooltip from "@zag-js/tooltip"
import { normalizeProps, useMachine } from "@zag-js/vue"
import { computed, defineComponent } from "vue"
export default defineComponent({
setup() {
const service = useMachine(tooltip.machine, { id: "1" })
const api = computed(() => tooltip.connect(service, normalizeProps))
return () => (
<button {...api.value.getTriggerProps()}>悬停我</button>
{api.value.open && (
<div {...api.value.getPositionerProps()}>
<div {...api.value.getContentProps()}>提示内容</div>
</div>
)}
)
}
})
Solid.js中使用
import * as tooltip from "@zag-js/tooltip"
import { normalizeProps, useMachine } from "@zag-js/solid"
import { createMemo, createUniqueId, Show } from "solid-js"
function Tooltip() {
const service = useMachine(tooltip.machine, { id: createUniqueId() })
const api = createMemo(() => tooltip.connect(service, normalizeProps))
return (
<div>
<button {...api().getTriggerProps()}>悬停我</button>
<Show when={api().open}>
<div {...api().getPositionerProps()}>
<div {...api().getContentProps()}>提示内容</div>
</div>
</Show>
</div>
)
}
Svelte中使用
<script lang="ts">
import * as tooltip from "@zag-js/tooltip"
import { useMachine, normalizeProps } from "@zag-js/svelte"
const service = useMachine(tooltip.machine, { id: "1" })
const api = $derived(tooltip.connect(service, normalizeProps))
</script>
<button {...api.getTriggerProps()}>悬停我</button>
{#if api.open}
<div {...api.getPositionerProps()}>
<div {...api.getContentProps()}>提示内容</div>
</div>
{/if}
属性标准化(normalizeProps)的重要性
不同前端框架在JSX属性命名上存在细微差异,Zag通过normalizeProps
函数自动处理这些差异:
-
事件监听器命名:
- React/Solid:
onKeyDown
- Vue:
onKeydown
- React/Solid:
-
样式属性:
- React:
{ marginBottom: 4 }
- Solid:
{ "margin-bottom": "4px" }
- Vue:
{ marginBottom: "4px" }
- React:
normalizeProps
会自动将这些差异转换为对应框架期望的格式,开发者无需关心底层实现细节。
状态机概念
Zag的核心是基于状态机的设计理念。每个UI组件都被建模为一个状态机,包含:
- 明确的状态(state):如"open"、"closed"等
- 状态转换(transition):由用户交互触发
- 副作用(effects):状态变化时执行的操作
这种设计使得组件行为更加可预测,也更易于测试和维护。
总结
Zag为前端开发者提供了一种全新的UI开发方式,通过状态机抽象交互逻辑,实现跨框架复用。无论你的项目使用React、Vue、Solid还是Svelte,都可以通过相似的API获得一致的开发体验。
对于初学者,建议从一个简单组件(如Tooltip)开始,逐步熟悉状态机的概念和Zag的工作方式。随着对状态机理解的深入,你将能够更高效地构建复杂且可访问性良好的UI组件。