在 React 項(xiàng)目中優(yōu)雅實(shí)現(xiàn)新用戶引導(dǎo)HagiCode 的 driver.js 實(shí)踐指南
在 React 項(xiàng)目中優(yōu)雅實(shí)現(xiàn)新用戶引導(dǎo):HagiCode 的 driver.js 實(shí)踐
當(dāng)用戶第一次打開你的產(chǎn)品時(shí),他們真的知道該從哪里開始嗎?這篇文章聊聊我們?cè)?HagiCode 項(xiàng)目里用 driver.js 做新用戶引導(dǎo)的那些事兒,也算是拋磚引玉罷了。
背景
你有沒有遇到過這樣的場(chǎng)景:新用戶注冊(cè)了你的產(chǎn)品,打開頁(yè)面后一臉茫然,東張西望,不知道該點(diǎn)哪里、該做什么。作為開發(fā)者,我們總以為用戶會(huì)"自己探索",畢竟人的好奇心是無限的嘛。可現(xiàn)實(shí)是——大部分用戶會(huì)在幾分鐘內(nèi)因?yàn)檎也坏饺肟诙那碾x開,就像故事開始得突然,結(jié)束得也自然。
新用戶引導(dǎo)是解決這個(gè)問題的重要手段,只是實(shí)現(xiàn)起來也不那么簡(jiǎn)單。一個(gè)好的引導(dǎo)系統(tǒng)需要:
- 能夠精準(zhǔn)定位頁(yè)面元素并高亮顯示
- 支持多步驟引導(dǎo)流程
- 能夠記住用戶的選擇(完成/跳過)
- 不影響頁(yè)面性能和正常交互
- 代碼結(jié)構(gòu)清晰,易于維護(hù)
在開發(fā) HagiCode 的過程中,我們也遇到了同樣的挑戰(zhàn)。HagiCode 是一個(gè) AI 代碼助手項(xiàng)目,核心工作流是"用戶創(chuàng)建提案 → AI 生成計(jì)劃 → 用戶審核 → AI 執(zhí)行"這樣一套 OpenSpec 流程。對(duì)于第一次接觸這個(gè)概念的用戶來說,這套流程是全新的,必須有一個(gè)好的引導(dǎo)來幫助他們快速上手。畢竟,新事物總是需要一點(diǎn)時(shí)間的。
關(guān)于 HagiCode
本文分享的方案來自我們?cè)?HagiCode 項(xiàng)目中的實(shí)踐經(jīng)驗(yàn)。HagiCode 是一個(gè)基于 Claude 的 AI 代碼助手,通過 OpenSpec 工作流幫助開發(fā)者更高效地完成代碼任務(wù)。你可以在 GitHub 上查看我們的開源代碼。
為什么選擇 driver.js
在技術(shù)選型階段,我們?cè)u(píng)估了幾個(gè)主流的引導(dǎo)庫(kù),怎么說呢,每個(gè)都有自己的特點(diǎn):
- Intro.js:功能強(qiáng)大但體積較大,樣式定制相對(duì)復(fù)雜
- Shepherd.js:API 設(shè)計(jì)很好,但對(duì)于我們的場(chǎng)景來說有點(diǎn)"重"
- driver.js:輕量、簡(jiǎn)潔、API 直觀,且支持 React 生態(tài)
最終我們選擇了 driver.js,其實(shí)也沒什么特別的理由,主要基于以下幾點(diǎn)考慮:
- 輕量級(jí):核心庫(kù)體積小,不會(huì)顯著增加打包體積
- API 簡(jiǎn)潔:配置項(xiàng)清晰直觀,上手快
- 靈活性:支持自定義定位、樣式和交互行為
- 動(dòng)態(tài)導(dǎo)入:可以按需加載,不影響首屏性能
選型這件事,其實(shí)沒有最好的,只有最合適的罷了。
技術(shù)實(shí)現(xiàn)
核心配置
driver.js 的配置非常直觀,以下是 HagiCode 項(xiàng)目中的核心配置:
import { driver } from 'driver.js';
import 'driver.js/dist/driver.css';
const newConversationDriver = driver({
allowClose: true, // 允許用戶關(guān)閉引導(dǎo)
animate: true, // 啟用動(dòng)畫效果
overlayClickBehavior: 'close', // 點(diǎn)擊遮罩層關(guān)閉引導(dǎo)
disableActiveInteraction: false, // 保持元素可交互
showProgress: false, // 不顯示進(jìn)度條(我們有自定義進(jìn)度管理)
steps: guideSteps // 引導(dǎo)步驟數(shù)組
});這些配置背后的考慮是:
allowClose: true- 尊重用戶選擇,不強(qiáng)制完成引導(dǎo),畢竟強(qiáng)扭的瓜不甜disableActiveInteraction: false- 某些步驟需要用戶實(shí)際操作(如輸入文字),所以不能禁用交互overlayClickBehavior: 'close'- 給用戶一個(gè)快速的退出方式
狀態(tài)管理
引導(dǎo)狀態(tài)的持久化是關(guān)鍵——我們不希望每次刷新頁(yè)面都重新引導(dǎo),那樣挺煩人的。HagiCode 使用 localStorage 來管理引導(dǎo)狀態(tài):
export type GuideState = 'pending' | 'dismissed' | 'completed';
export interface UserGuideState {
session: GuideState;
detailGuides: Record<string, GuideState>;
}
// 讀取狀態(tài)
export const getUserGuideState = (): UserGuideState => {
const state = localStorage.getItem('userGuideState');
return state ? JSON.parse(state) : { session: 'pending', detailGuides: {} };
};
// 更新狀態(tài)
export const setUserGuideState = (state: UserGuideState) => {
localStorage.setItem('userGuideState', JSON.stringify(state));
};我們定義了三種狀態(tài):
pending:引導(dǎo)進(jìn)行中,用戶還未完成或跳過dismissed:用戶主動(dòng)關(guān)閉了引導(dǎo)completed:用戶完成了所有步驟
對(duì)于提案詳情頁(yè)的引導(dǎo),我們還支持更細(xì)粒度的狀態(tài)追蹤(通過 detailGuides 字典),因?yàn)橐粋€(gè)提案可能會(huì)經(jīng)歷多個(gè)階段(草稿、審核、執(zhí)行完成),每個(gè)階段都需要不同的引導(dǎo)。畢竟,事情的狀態(tài)總是在變化的。
目標(biāo)元素定位
driver.js 使用 CSS 選擇器來定位目標(biāo)元素。HagiCode 采用了一個(gè)約定:使用 data-guide 自定義屬性來標(biāo)記引導(dǎo)目標(biāo):
const steps = [
{
element: '[data-guide="launch"]',
popover: {
title: '開始新對(duì)話',
description: '點(diǎn)擊這里創(chuàng)建一個(gè)新的對(duì)話會(huì)話...'
}
}
];在組件中這樣使用:
<button data-guide="launch" onClick={handleLaunch}>
新建對(duì)話
</button>這種做法的好處是:
- 避免與業(yè)務(wù)樣式類名沖突
- 語義清晰,一眼就能看出這個(gè)元素與引導(dǎo)相關(guān)
- 便于統(tǒng)一管理和維護(hù)
動(dòng)態(tài)導(dǎo)入優(yōu)化
因?yàn)橐龑?dǎo)功能只在特定場(chǎng)景下才需要(比如新用戶第一次訪問),我們采用動(dòng)態(tài)導(dǎo)入來優(yōu)化初始加載性能:
const initNewUserGuide = async () => {
// 動(dòng)態(tài)導(dǎo)入 driver.js
const { driver } = await import('driver.js');
await import('driver.js/dist/driver.css');
// 初始化引導(dǎo)
const newConversationDriver = driver({
// ...配置
});
newConversationDriver.drive();
};這樣 driver.js 及其樣式文件只會(huì)在需要時(shí)才加載,不會(huì)影響首屏性能。畢竟,誰愿意為暫時(shí)用不到的東西付出等待的代價(jià)呢?
引導(dǎo)流程設(shè)計(jì)
HagiCode 實(shí)現(xiàn)了兩條引導(dǎo)路徑,覆蓋了用戶的核心使用場(chǎng)景。
會(huì)話引導(dǎo)(10步)
這條引導(dǎo)幫助用戶完成從創(chuàng)建對(duì)話到提交第一個(gè)完整提案的整個(gè)流程:
- launch - 啟動(dòng)引導(dǎo),介紹"新建對(duì)話"按鈕
- compose - 引導(dǎo)用戶在輸入框中輸入請(qǐng)求
- send - 引導(dǎo)點(diǎn)擊發(fā)送按鈕
- proposal-launch-readme - 引導(dǎo)創(chuàng)建 README 提案
- proposal-compose-readme - 引導(dǎo)編輯 README 請(qǐng)求內(nèi)容
- proposal-submit-readme - 引導(dǎo)提交 README 提案
- proposal-launch-agents - 引導(dǎo)創(chuàng)建 AGENTS.md 提案
- proposal-compose-agents - 引導(dǎo)編輯 AGENTS.md 請(qǐng)求
- proposal-submit-agents - 引導(dǎo)提交 AGENTS.md 提案
- proposal-wait - 說明 AI 正在處理,請(qǐng)稍候
這條引導(dǎo)的設(shè)計(jì)思路是:通過兩個(gè)實(shí)際的提案創(chuàng)建任務(wù)(README 和 AGENTS.md),讓用戶親手體驗(yàn) HagiCode 的核心工作流。畢竟,紙上得來終覺淺,絕知此事要躬行。
下面這幾張圖,對(duì)應(yīng)的就是會(huì)話引導(dǎo)里的幾個(gè)關(guān)鍵節(jié)點(diǎn):

會(huì)話引導(dǎo)的第一步,先把用戶帶到“新建普通會(huì)話”的入口上。

接著引導(dǎo)用戶在輸入框里寫下第一句請(qǐng)求,降低第一次開口的門檻。

輸入完成后,再明確提示用戶發(fā)送第一條消息,讓操作路徑更連貫。

當(dāng)兩個(gè)提案都創(chuàng)建完成后,引導(dǎo)會(huì)回到會(huì)話列表,讓用戶知道接下來只需要等待系統(tǒng)繼續(xù)執(zhí)行和刷新。
提案詳情引導(dǎo)(3步)
當(dāng)用戶進(jìn)入提案詳情頁(yè)時(shí),根據(jù)提案的當(dāng)前狀態(tài)觸發(fā)對(duì)應(yīng)的引導(dǎo):
- drafting(草稿階段)- 引導(dǎo)用戶查看 AI 生成的計(jì)劃
- reviewing(審核階段)- 引導(dǎo)用戶執(zhí)行計(jì)劃
- executionCompleted(完成階段)- 引導(dǎo)用戶歸檔計(jì)劃
這條引導(dǎo)的特點(diǎn)是狀態(tài)驅(qū)動(dòng)——根據(jù)提案的實(shí)際狀態(tài)動(dòng)態(tài)決定顯示哪個(gè)引導(dǎo)步驟。事物總是在變化,引導(dǎo)也應(yīng)該跟著變化才是。
下面這張圖展示的是提案詳情頁(yè)在“起草階段”的引導(dǎo)狀態(tài):

在這個(gè)階段,引導(dǎo)會(huì)把用戶注意力聚焦到“生成規(guī)劃”這個(gè)關(guān)鍵動(dòng)作上,避免第一次進(jìn)入詳情頁(yè)時(shí)不知道該先做什么。
元素渲染重試機(jī)制
在 React 應(yīng)用中,引導(dǎo)目標(biāo)元素可能還沒渲染完成(比如等待異步數(shù)據(jù)加載)。為了處理這種情況,HagiCode 實(shí)現(xiàn)了一個(gè)重試機(jī)制:
const waitForElement = (selector: string, maxRetries = 10, interval = 100) => {
let retries = 0;
return new Promise<HTMLElement>((resolve, reject) => {
const checkElement = () => {
const element = document.querySelector(selector) as HTMLElement;
if (element) {
resolve(element);
} else if (retries < maxRetries) {
retries++;
setTimeout(checkElement, interval);
} else {
reject(new Error(`Element not found: ${selector}`));
}
};
checkElement();
});
};
在初始化引導(dǎo)前調(diào)用這個(gè)函數(shù),確保目標(biāo)元素已經(jīng)存在。有時(shí)候,多等待一下也是值得的。
最佳實(shí)踐總結(jié)
基于 HagiCode 的實(shí)踐經(jīng)驗(yàn),這里分享幾個(gè)關(guān)鍵的最佳實(shí)踐:
1. 引導(dǎo)應(yīng)該是"可逃離的"
不要強(qiáng)制用戶完成引導(dǎo)。有些用戶是探索型的,他們更喜歡自己摸索。提供清晰的"跳過"按鈕,并記住用戶的選擇,下次不再打擾。畢竟,美的事物或人,不一定要占有,只要她還是美的,自己好好看著她的美就好了。
2. 引導(dǎo)內(nèi)容要簡(jiǎn)潔有力
每個(gè)引導(dǎo)步驟應(yīng)該聚焦于單一目標(biāo):
- Title:簡(jiǎn)短清晰,不超過 10 個(gè)字
- Description:直擊要點(diǎn),告訴用戶"這是啥"和"為啥要用"
避免長(zhǎng)篇大論的說明——用戶在引導(dǎo)階段的注意力是很有限的。話說多了,反而沒人愿意看。
3. 選擇器要穩(wěn)定
使用穩(wěn)定的、不頻繁變化的元素標(biāo)記方式。data-guide 自定義屬性是一個(gè)好選擇,避免依賴 class 名或 DOM 結(jié)構(gòu),因?yàn)檫@些很容易在重構(gòu)中變化。代碼總是在變化的,但有些東西應(yīng)該盡量保持穩(wěn)定。
4. 測(cè)試你的引導(dǎo)
HagiCode 為引導(dǎo)功能編寫了完整的測(cè)試用例:
describe('NewUserConversationGuide', () => {
it('應(yīng)該正確初始化引導(dǎo)狀態(tài)', () => {
const state = getUserGuideState();
expect(state.session).toBe('pending');
});
it('應(yīng)該正確更新引導(dǎo)狀態(tài)', () => {
setUserGuideState({ session: 'completed', detailGuides: {} });
const state = getUserGuideState();
expect(state.session).toBe('completed');
});
});測(cè)試可以確保在重構(gòu)代碼時(shí)不會(huì)不小心破壞引導(dǎo)功能。畢竟,誰也不希望改點(diǎn)代碼就把之前的功能搞壞了。
5. 性能優(yōu)化
- 使用動(dòng)態(tài)導(dǎo)入延遲加載引導(dǎo)庫(kù)
- 避免在用戶已經(jīng)完成引導(dǎo)后仍然初始化引導(dǎo)邏輯
- 考慮引導(dǎo)動(dòng)畫的性能影響,低端設(shè)備上可以關(guān)閉動(dòng)畫
性能這東西,就像生活一樣,該省的地方還是要省的。
總結(jié)
新用戶引導(dǎo)是提升產(chǎn)品用戶體驗(yàn)的重要環(huán)節(jié)。在 HagiCode 項(xiàng)目中,我們使用 driver.js 構(gòu)建了一套完整的引導(dǎo)系統(tǒng),覆蓋了從會(huì)話創(chuàng)建到提案執(zhí)行的整個(gè)工作流。
通過本文的分享,我們希望傳達(dá)的核心觀點(diǎn)是:
- 技術(shù)選型要匹配需求:driver.js 不是最強(qiáng)的,但對(duì)我們來說是最合適的
- 狀態(tài)管理很關(guān)鍵:用 localStorage 持久化引導(dǎo)狀態(tài),避免重復(fù)打擾用戶
- 引導(dǎo)設(shè)計(jì)要聚焦:每個(gè)步驟解決一個(gè)問題,不要貪多
- 代碼結(jié)構(gòu)要清晰:分離引導(dǎo)配置、狀態(tài)管理和 UI 邏輯,便于維護(hù)
如果你正在為自己的項(xiàng)目添加新用戶引導(dǎo)功能,希望本文的實(shí)踐經(jīng)驗(yàn)?zāi)軐?duì)你有所幫助。其實(shí)技術(shù)這東西,也沒什么神秘的,多嘗試,多總結(jié),慢慢就好了......
參考資料
原文與版權(quán)說明
感謝您的閱讀,如果您覺得本文有用,歡迎點(diǎn)贊、收藏和分享支持。
本內(nèi)容采用人工智能輔助協(xié)作,最終內(nèi)容由作者審核并確認(rèn)。
- 本文作者: newbe36524
- 原文鏈接: https://docs.hagicode.com/go?platform=cnblogs&target=%2Fblog%2F2026-04-01-new-user-guide-with-driverjs%2F
- 版權(quán)聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協(xié)議。轉(zhuǎn)載請(qǐng)注明出處!
到此這篇關(guān)于在 React 項(xiàng)目中優(yōu)雅實(shí)現(xiàn)新用戶引導(dǎo)HagiCode 的 driver.js 實(shí)踐指南的文章就介紹到這了,更多相關(guān)React HagiCode 的 driver.js內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React+hook實(shí)現(xiàn)聯(lián)動(dòng)模糊搜索
這篇文章主要為大家詳細(xì)介紹了如何利用React+hook+antd實(shí)現(xiàn)聯(lián)動(dòng)模糊搜索功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02
React Router 5.1.0使用useHistory做頁(yè)面跳轉(zhuǎn)導(dǎo)航的實(shí)現(xiàn)
本文主要介紹了React Router 5.1.0使用useHistory做頁(yè)面跳轉(zhuǎn)導(dǎo)航的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
React+Vite中利用Fetch將CSV數(shù)據(jù)轉(zhuǎn)成JSON字符串
在一些小型項(xiàng)目中,前端可能需要直接處理 CSV 文件數(shù)據(jù),將其轉(zhuǎn)換為 JSON 字符串后再進(jìn)行邏輯操作和展示,本文將會(huì)介紹兩種方法,需要的朋友可以參考下2025-12-12
react.js使用webpack搭配環(huán)境的入門教程
本文主要介紹了react 使用webpack搭配環(huán)境的入門教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-08-08
react用Redux中央倉(cāng)庫(kù)實(shí)現(xiàn)一個(gè)todolist
這篇文章主要為大家詳細(xì)介紹了react用Redux中央倉(cāng)庫(kù)實(shí)現(xiàn)一個(gè)todolist,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09

