Skip to content

React Grab 使用文档

📖 项目简介

React Grab 是一个专为前端开发的开发者工具,允许你直接从网站页面上选择 UI 元素,快速获取其代码上下文信息(包括文件名、组件名和 HTML 源码),并将其复制到剪贴板。

核心价值

  • 提升 AI 编码效率:为 Trae、Cursor、Claude Code、Copilot 等 AI 编码工具提供精确的元素上下文,使其运行速度提升高达 3 倍
  • 精准定位:快速定位 UI 元素对应的源代码位置
  • 零配置使用:安装后即可使用,无需复杂配置

工作原理

在浏览器中悬停在任何 UI 元素上,按下快捷键即可复制该元素的上下文信息:

  • Mac: ⌘C (Cmd+C)
  • Windows/Linux: Ctrl+C (Windows/Linux)

复制的信息格式示例:

js
<a class="ml-auto inline-block text-sm" href="#">
  Forgot your password?
</a>
in LoginForm at components/login-form.tsx:46:19

🚀 使用方法

react-grab 示例

1. 启动开发服务器

确保在开发环境(NODE_ENV=development)下运行项目。

2. 选择元素

  • 在浏览器中打开你的应用
  • 将鼠标悬停在任意 UI 元素上
  • 按下 ⌘C (Mac) 或 Ctrl+C (Windows/Linux)

3. 粘贴到 Trae 编辑器

复制的信息会自动包含:

  • 元素的 HTML 源码
  • React 组件名称
  • 文件路径和行号

直接粘贴到 Trae 的对话框中即可进行精确的代码修改。


🔌 插件系统

通过插件可以扩展 React Grab 的功能,包括:

  • 右键菜单操作
  • 工具栏菜单项
  • 生命周期钩子
  • 主题覆盖

注册插件

js
import { registerPlugin } from "react-grab";

registerPlugin({
  name: "my-plugin",
  hooks: {
    onElementSelect: (element) => {
      console.log("Selected:", element.tagName);
    },
  },
});

在 React 中使用

jsx
import { registerPlugin, unregisterPlugin } from "react-grab";

useEffect(() => {
  registerPlugin({
    name: "my-plugin",
    actions: [
      {
        id: "my-action",
        label: "My Action",
        shortcut: "M",
        onAction: (context) => {
          console.log("Action on:", context.element);
          context.hideContextMenu();
        },
      },
    ],
  });

  return () => unregisterPlugin("my-plugin");
}, []);

自定义操作位置

使用 target 字段控制操作显示位置:

js
actions: [
  {
    id: "inspect",
    label: "Inspect",
    shortcut: "I",
    onAction: (ctx) => console.dir(ctx.element),
    // 默认在右键菜单中显示
  },
  {
    id: "toggle-freeze",
    label: "Freeze",
    target: "toolbar",  // 在工具栏下拉菜单中显示
    isActive: () => isFrozen,
    onAction: () => toggleFreeze(),
  },
];

🧩 原始 API(Primitives)

使用原始 API 可以完全自定义元素选择器,不依赖 React Grab 的默认 UI。

禁用自动初始化

如果只想使用原始 API 而不显示默认覆盖层:

html
<script>
  window.__REACT_GRAB_DISABLED__ = true;
</script>

自定义元素选择器示例

tsx
import { useState } from "react";
import {
  getElementContext,
  freeze,
  unfreeze,
  openFile,
  type ReactGrabElementContext,
} from "react-grab/primitives";

const useElementSelector = (
  onSelect: (context: ReactGrabElementContext) => void,
) => {
  const [isActive, setIsActive] = useState(false);

  const startSelecting = () => {
    setIsActive(true);

    // 创建高亮覆盖层
    const highlightOverlay = document.createElement("div");
    Object.assign(highlightOverlay.style, {
      position: "fixed",
      pointerEvents: "none",
      zIndex: "999999",
      border: "2px solid #3b82f6",
      transition: "all 75ms ease-out",
      display: "none",
    });
    document.body.appendChild(highlightOverlay);

    // 鼠标移动处理
    const handleMouseMove = ({ clientX, clientY }: MouseEvent) => {
      highlightOverlay.style.display = "none";
      const target = document.elementFromPoint(clientX, clientY);
      if (!target) return;
      const { top, left, width, height } = target.getBoundingClientRect();
      Object.assign(highlightOverlay.style, {
        top: `${top}px`,
        left: `${left}px`,
        width: `${width}px`,
        height: `${height}px`,
        display: "block",
      });
    };

    // 点击处理
    const handleClick = async ({ clientX, clientY }: MouseEvent) => {
      highlightOverlay.style.display = "none";
      const target = document.elementFromPoint(clientX, clientY);
      teardown();
      if (!target) return;
      freeze();
      onSelect(await getElementContext(target));
      unfreeze();
    };

    const teardown = () => {
      document.removeEventListener("mousemove", handleMouseMove);
      document.removeEventListener("click", handleClick, true);
      highlightOverlay.remove();
      setIsActive(false);
    };

    document.addEventListener("mousemove", handleMouseMove);
    document.addEventListener("click", handleClick, true);
  };

  return { isActive, startSelecting };
};

// 使用示例
const ElementSelector = () => {
  const [context, setContext] = useState<ReactGrabElementContext | null>(null);
  const selector = useElementSelector(setContext);

  return (
    <div>
      <button onClick={selector.startSelecting} disabled={selector.isActive}>
        {selector.isActive ? "Selecting…" : "Select Element"}
      </button>
      {context && (
        <div>
          <p>Component: {context.componentName}</p>
          <p>Selector: {context.selector}</p>
          <pre>{context.stackString}</pre>
          <button
            onClick={() => {
              const frame = context.stack[0];
              if (frame?.fileName) openFile(frame.fileName, frame.lineNumber);
            }}
          >
            Open File
          </button>
        </div>
      )}
    </div>
  );
};

🔗 MCP 集成

连接 MCP(Model Context Protocol)以增强 AI 编码工具的集成:

bash
npx -y grab@latest add mcp

📝 可用 API

核心导出

API说明
registerPlugin(plugin)注册插件
unregisterPlugin(name)注销插件
getElementContext(element)获取元素的上下文信息
freeze()冻结当前页面状态
unfreeze()解除冻结
openFile(fileName, lineNumber)在编辑器中打开文件

元素上下文结构

typescript
interface ReactGrabElementContext {
  element: HTMLElement;           // DOM 元素
  componentName: string;          // React 组件名
  selector: string;               // CSS 选择器
  stackString: string;            // 调用栈字符串
  stack: Array<{
    fileName: string;             // 文件名
    lineNumber: number;           // 行号
  }>;
  hideContextMenu: () => void;    // 隐藏右键菜单
}

💡 使用场景

1. Trae AI 辅助开发

  • 快速向 Trae 提供精确的 UI 元素信息
  • 减少手动查找文件的时间
  • 提高代码修改的准确性

2. 代码审查

  • 快速定位 UI 元素对应的源代码
  • 理解组件结构和文件位置

3. 调试和排查

  • 识别未知组件的来源
  • 追踪 UI 问题的根源

⚠️ 注意事项

  1. 仅开发环境使用:React Grab 默认只在开发环境(NODE_ENV=development)下激活
  2. 生产环境禁用:确保在生产环境中不加载此工具
  3. 浏览器兼容性:需要现代浏览器支持

📚 相关资源


📄 许可证

MIT License


文档生成日期:2026 年 3 月 19 日