# React 与 Vue 集成

URL: https://caijiao.org/driver-js/04-frameworks/01-react-vue
Source: docs/driver-js/04-frameworks/01-react-vue.md
Description: 讲解 Driver.js 在 React 和 Vue 项目中的初始化、ref 目标元素、组件卸载清理、路由切换和常见封装方式。

Driver.js 不依赖框架。集成到 React 或 Vue 时，重点不是 API 本身，而是处理好组件生命周期、DOM 渲染时机和清理逻辑。

## React 示例

React 中通常在 `useEffect` 里创建实例，并在组件卸载时销毁。

```jsx
import { useEffect, useRef } from "react";
import { driver } from "driver.js";
import "driver.js/dist/driver.css";

export function DashboardHelp() {
  const createButtonRef = useRef(null);

  useEffect(() => {
    const driverObj = driver({
      showProgress: true,
      steps: [
        {
          element: createButtonRef.current,
          popover: {
            title: "创建项目",
            description: "点击这里创建你的第一个项目。",
          },
        },
        {
          element: "#project-list",
          popover: {
            title: "项目列表",
            description: "所有项目都会显示在这里。",
          },
        },
      ],
    });

    return () => {
      driverObj.destroy();
    };
  }, []);

  return <button ref={createButtonRef}>创建项目</button>;
}
```

如果实例只在点击帮助按钮时使用，也可以把创建逻辑放到事件处理函数中。

## React 点击启动

```jsx
import { driver } from "driver.js";
import "driver.js/dist/driver.css";

const steps = [
  {
    element: "#create-project",
    popover: {
      title: "创建项目",
      description: "从这里开始创建项目。",
    },
  },
];

export function HelpButton() {
  function startTour() {
    const driverObj = driver({ steps });
    driverObj.drive();
  }

  return <button onClick={startTour}>页面引导</button>;
}
```

这种写法简单，但如果导览过程中路由切换，仍然需要在路由层处理清理。

## Vue 示例

Vue 3 中可以在 `onMounted` 后访问 DOM，在 `onBeforeUnmount` 中销毁实例。

```vue

<template>
  <button ref="createButton">创建项目</button>
  <button @click="startTour">页面引导</button>
</template>
```

## 路由切换清理

单页应用中，导览可能跨页面或被路由打断。建议在路由切换前销毁当前实例：

```js
router.beforeEach(() => {
  activeDriver?.destroy();
  activeDriver = null;
});
```

如果导览确实需要跨页面，最好拆成多个页面内导览，而不是让一个 tour 依赖多次路由跳转。

## 封装建议

中大型项目可以封装一个 `startTour(name)` 方法：

```js
const tours = {
  dashboard: [
    {
      element: "#create-project",
      popover: {
        title: "创建项目",
        description: "从这里创建项目。",
      },
    },
  ],
};

let activeDriver;

export function startTour(name) {
  activeDriver?.destroy();

  activeDriver = driver({
    showProgress: true,
    steps: tours[name] || [],
  });

  activeDriver.drive();
}
```

这样可以统一处理中文按钮、埋点、已读状态和销毁逻辑。
