首页
/ Chakra UI/Zag 项目入门指南:跨框架UI状态管理

Chakra UI/Zag 项目入门指南:跨框架UI状态管理

2025-07-08 06:29:54作者:羿妍玫Ivan

什么是Zag?

Zag是一个基于状态机的UI组件库,它通过将交互逻辑抽象为状态图表(statechart)的方式,为开发者提供可复用的组件交互模式。该项目最大的特点是"一次编写,多框架运行",目前支持React、Vue 3、Solid.js和Svelte等主流前端框架。

核心优势

  1. 跨框架兼容:同一套交互逻辑可在不同框架中使用
  2. 状态机驱动:清晰的组件状态管理
  3. 无障碍支持:内置可访问性最佳实践
  4. 类型安全:完整的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函数自动处理这些差异:

  1. 事件监听器命名

    • React/Solid: onKeyDown
    • Vue: onKeydown
  2. 样式属性

    • React: { marginBottom: 4 }
    • Solid: { "margin-bottom": "4px" }
    • Vue: { marginBottom: "4px" }

normalizeProps会自动将这些差异转换为对应框架期望的格式,开发者无需关心底层实现细节。

状态机概念

Zag的核心是基于状态机的设计理念。每个UI组件都被建模为一个状态机,包含:

  • 明确的状态(state):如"open"、"closed"等
  • 状态转换(transition):由用户交互触发
  • 副作用(effects):状态变化时执行的操作

这种设计使得组件行为更加可预测,也更易于测试和维护。

总结

Zag为前端开发者提供了一种全新的UI开发方式,通过状态机抽象交互逻辑,实现跨框架复用。无论你的项目使用React、Vue、Solid还是Svelte,都可以通过相似的API获得一致的开发体验。

对于初学者,建议从一个简单组件(如Tooltip)开始,逐步熟悉状态机的概念和Zag的工作方式。随着对状态机理解的深入,你将能够更高效地构建复杂且可访问性良好的UI组件。