headlessui之Dialog

半兽人 发表于: 2025-08-01   最后更新时间: 2025-08-01 17:18:22  
{{totalSubscript}} 订阅, 26 游览

React 项目中,@headlessui/react 是一个由 Tailwind Labs 开发的无样式、可访问性良好的 UI 组件库,常用于构建完全自定义风格的对话框(Dialog)、菜单、列表等。

@headlessui/react 提供的 DialogDialogBackdropDialogTitle 是用来创建无障碍对话框(Modal)的核心组件。下面我给你详细介绍它们的作用和用法,并提供一个完整示例。

1. 基本概念

1.1 Dialog

  • 核心容器组件,包裹整个对话框内容。
  • 自动处理:

    • role="dialog"aria-modal="true"
    • 焦点锁定(focus trap)
    • ESC 键关闭(可选)

1.2 DialogBackdrop

  • 对话框的背景遮罩层
  • 通常覆盖整个视口,阻止用户点击其他区域。

1.3 DialogTitle

  • 对话框的标题,自动关联到 aria-labelledby,提升无障碍性。

2. 安装

npm install @headlessui/react
# 或
yarn add @headlessui/react

3. 完整示例

import { useState, Fragment } from 'react';
import { Dialog, DialogBackdrop, DialogTitle } from '@headlessui/react';

export default function MyDialog() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button
        onClick={() => setIsOpen(true)}
        className="px-4 py-2 bg-blue-500 text-white rounded"
      >
        打开对话框
      </button>

      <Dialog open={isOpen} onClose={() => setIsOpen(false)} className="relative z-50">
        {/* 背景遮罩 */}
        <DialogBackdrop className="fixed inset-0 bg-black/50" />

        {/* 对话框内容 */}
        <div className="fixed inset-0 flex items-center justify-center p-4">
          <Dialog.Panel className="mx-auto max-w-sm rounded bg-white p-6 shadow-lg">
            <DialogTitle className="text-lg font-bold">
              提示信息
            </DialogTitle>
            <p className="mt-2 text-gray-600">
              这是使用 @headlessui/react 创建的无障碍对话框。
            </p>

            <div className="mt-4 flex justify-end gap-2">
              <button
                className="px-4 py-2 rounded bg-gray-200 hover:bg-gray-300"
                onClick={() => setIsOpen(false)}
              >
                取消
              </button>
              <button
                className="px-4 py-2 rounded bg-blue-500 text-white hover:bg-blue-600"
                onClick={() => setIsOpen(false)}
              >
                确定
              </button>
            </div>
          </Dialog.Panel>
        </div>
      </Dialog>
    </>
  );
}

4. 关键点说明

  1. Dialog

    • open 控制是否显示
    • onClose 用于关闭逻辑(点击遮罩或按 ESC 时触发)
  2. DialogBackdrop

    • 必须直接作为 Dialog 的子组件
    • 负责遮罩层,默认无动画,可结合 Transition 添加渐入渐出效果
  3. DialogTitle

    • 作为标题标签
    • 自动关联到 aria-labelledby,提升可访问性
  4. Dialog.Panel

    • 对话框的内容面板(注意 @headlessui/react 的最新版本用 Dialog.Panel,旧版本可以直接写内容)

5. 可选增强

如果你想要动画,可以结合 Transition 组件使用:

import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Transition } from '@headlessui/react'

<Transition show={isOpen}>
  <Dialog ...>
    <Transition.Child
      enter="ease-out duration-300"
      enterFrom="opacity-0"
      enterTo="opacity-100"
      leave="ease-in duration-200"
      leaveFrom="opacity-100"
      leaveTo="opacity-0"
    >
      <DialogBackdrop className="fixed inset-0 bg-black/50" />
    </Transition.Child>

    <Transition.Child
      enter="ease-out duration-300"
      enterFrom="opacity-0 scale-95"
      enterTo="opacity-100 scale-100"
      leave="ease-in duration-200"
      leaveFrom="opacity-100 scale-100"
      leaveTo="opacity-0 scale-95"
    >
      <DialogPanel className="fixed inset-0 flex items-center justify-center">
        ...
      </DialogPanel>
    </Transition.Child>
  </Dialog>
</Transition>

这样可以让背景和对话框都有过渡动画。

6. 总结

  • Dialog:核心容器,提供无障碍、焦点管理、ESC 关闭等功能
  • DialogBackdrop:背景遮罩,阻止点击外部区域
  • DialogTitle:标题,自动关联 ARIA,提高无障碍性
  • 常配合 Dialog.PanelTransition 使用,实现完整的模态弹窗
更新于 2025-08-01

查看Headless UI更多相关的文章或提一个关于Headless UI的问题,也可以与我们一起分享文章