ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Airi 项目中使用 VueUse useStepper 构建多步骤向导界面的完整指南

2026/9/11 10:17:16 拓冰建站 浏览量
Airi 项目中使用 VueUse useStepper 构建多步骤向导界面的完整指南 Airi 项目中使用 VueUse useStepper 构建多步骤向导界面的完整指南【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi导读useStepper是 VueUse 提供的一个 Utility 类组合式函数它把多步骤向导multi-step wizard的状态管理封装为一组语义化、类型安全的工具方法。本文以当前仓库中 .agents/skills/vueuse-functions/references/useStepper.md 为骨架结合仓库内 Vue 3 应用stage-web、stage-tamagotchi、stage-pocket 等与packages/stage-ui组件库中 Steppers 组件的实际用法系统讲解useStepper的两种输入形态、全部返回值与导航 API、类型声明以及如何在向导类业务场景中落地实践。读完本文你将掌握用数组或对象两种方式声明步骤、用current/index驱动 UI 渲染、用goTo/goToNext/goBackTo等 API 实现任意跳转与条件跳过以及如何通过isNext/isBefore等判定函数控制按钮与步骤状态。一、useStepper 是什么把向导状态收敛为一个组合式函数在 Vue 应用中实现注册流程、设置向导、分步表单这类多步骤界面时通常需要手动维护以下状态与逻辑当前步骤是第几步索引或名称当前步骤、下一步、上一步分别是什么当前是否在第一步 / 最后一步前进、后退、跳转到指定步骤的操作函数判断某个步骤与当前步骤的前后关系。这些逻辑散落在各个组件里会造成大量重复代码。VueUse 的useStepper将这些状态与操作统一收敛为一次调用正如其在 vueuse-functions 技能索引 中描述的Provides helpers for building a multi-step wizard interface调用方式标记为AUTO即只要是 Vue.js / Nuxt 项目中需要多步骤交互就应优先使用它而不是手写状态机。在 Airi 仓库中vueuse/core通过 pnpm-workspace.yaml 中的 catalog 统一锁定为^14.4.0vueuse/motion为^3.0.3并被apps/component-calling、apps/stage-pocket、apps/stage-tamagotchi、apps/stage-web、apps/ui-server-auth等全部 Vue 前端应用引用因此useStepper可以直接从vueuse/core导入无需额外安装依赖。import { useStepper } from vueuse/core二、两种输入形态数组步骤与对象步骤useStepper的重载签名决定了它支持两种步骤声明方式对应两种典型的向导建模需求。2.1 步骤为数组只关心名称的线性流程当步骤本身只需要一个标识符如字符串或数字不需要附带额外元数据时传入字符串数组即可import { useStepper } from vueuse/core const { steps, stepNames, index, current, next, previous, isFirst, isLast, goTo, goToNext, goToPrevious, goBackTo, isNext, isPrevious, isCurrent, isBefore, isAfter, } useStepper([ billing-address, terms, payment, ]) // Access the step through current console.log(current.value) // billing-address此时current直接就是当前步骤的字符串名称。数组元素的类型被约束为string | number见下方类型声明中T extends string | number的重载保证步骤标识可被安全地用于v-if、:key或路由参数。2.2 步骤为对象携带标题、描述等元数据的步骤字典当每个步骤需要携带title、description、图标等展示数据时传入对象键为步骤名、值为任意类型即可import { useStepper } from vueuse/core const { steps, stepNames, index, current, next, previous, isFirst, isLast, goTo, goToNext, goToPrevious, goBackTo, isNext, isPrevious, isCurrent, isBefore, isAfter, } useStepper({ user-information: { title: User information, }, billing-address: { title: Billing address, }, terms: { title: Terms, }, payment: { title: Payment, }, }) // Access the step object through current console.log(current.value.title) // User information此时current返回的是当前步骤对应的值对象而非步骤名可直接访问对象的任意字段用于渲染。对象形态非常适合与仓库packages/stage-ui中 Steppers 组件的数据结构对齐——在 steppers.story.vue 中步骤数据同样采用{ id, title, description? }的对象数组建模例如 StageWeb 的 10 步新手引导setupWorkflowSteps每一步包含title如 Configure Provider与description操作提示文本。对象形态的useStepper恰好能把这种步骤名 → 展示元数据的映射交给组合式函数统一管理。2.3 两种形态的取舍形态输入类型current的值适用场景数组MaybeRefT[]T为string \| number步骤名字符串/数字线性流程、仅需步骤标识、步骤内容放在独立的template分支中对象MaybeRefRecordstring, any步骤名对应的值对象需要title/description/配置等元数据随步骤一起建模两者都接受MaybeRef输入即可以传入ref、reactive或普通数组/对象满足步骤列表本身也是响应式的场景例如动态增减步骤的向导。三、返回值与类型声明全解析useStepper的类型声明节选自 useStepper.md完整定义了返回对象的 17 个成员按用途可分为四类。3.1 状态与索引export interface UseStepperReturnStepName, Steps, Step { /** List of steps. */ steps: ReadonlyRefSteps /** List of step names. */ stepNames: ReadonlyRefStepName[] /** Index of the current step. */ index: Refnumber /** Current step. */ current: ComputedRefStep /** Next step, or undefined if the current step is the last one. */ next: ComputedRefStepName | undefined /** Previous step, or undefined if the current step is the first one. */ previous: ComputedRefStepName | undefined /** Whether the current step is the first one. */ isFirst: ComputedRefboolean /** Whether the current step is the last one. */ isLast: ComputedRefboolean // ... }steps完整的步骤集合只读 Ref。数组形态下即原始数组对象形态下即原始对象。stepNames所有步骤名的有序列表只读 Ref。用于渲染进度点 / 步骤条因为它在两种形态下都统一为数组。index当前步骤的下标可写 Ref从 0 开始。直接修改index.value也能切换步骤。current当前步骤。数组形态为步骤名对象形态为步骤对应的值对象两者均为ComputedRef模板中直接使用current.value。next/previous当前步骤的下一步 / 上一步名称处于边界时返回undefined可直接用于判断是否有下一步。isFirst/isLast是否为第一步 / 最后一步的布尔计算属性通常直接绑定Back按钮的禁用态与Next→Finish文案切换。3.2 查询 APIat 与 get/** Get the step at the specified index. */ at: (index: number) Step | undefined /** Get a step by the specified name. */ get: (step: StepName) Step | undefinedat(index)按下标取步骤越界返回undefined适合渲染第 N 步摘要。get(name)按名称取步骤。对象形态下返回该名称对应的值对象数组形态下返回步骤名本身。3.3 导航 APIgoTo / goToNext / goToPrevious / goBackTo/** Go to the specified step. */ goTo: (step: StepName) void /** Go to the next step. Does nothing if the current step is the last one. */ goToNext: () void /** Go to the previous step. Does nothing if the current step is the previous one. */ goToPrevious: () void /** Go back to the given step, only if the current step is after. */ goBackTo: (step: StepName) voidgoTo(name)直接跳到任意步骤名称不存在时行为取决于底层实现使用时应保证传入合法步骤名。goToNext()/goToPrevious()前进 / 后退一步goToNext在最后一步时什么都不做goToPrevious在第一步时什么都不做天然形成边界保护无需手动判断。goBackTo(name)仅当目标步骤在当前步骤之前时才跳转用于返回上一步修正的语义如从确认页回到填写页不会向前跳。3.4 判定函数isNext / isPrevious / isCurrent / isBefore / isAfter/** Checks whether the given step is the next step. */ isNext: (step: StepName) boolean /** Checks whether the given step is the previous step. */ isPrevious: (step: StepName) boolean /** Checks whether the given step is the current step. */ isCurrent: (step: StepName) boolean /** Checks if the current step is before the given step. */ isBefore: (step: StepName) boolean /** Checks if the current step is after the given step. */ isAfter: (step: StepName) boolean这组判定函数以步骤名为输入返回布尔值isCurrent(step)判断某步骤是否就是当前步骤等价于current.value step用于高亮当前步骤条。isNext(step)/isPrevious(step)判断某步骤是否是紧邻的下一步 / 上一步用于渲染上一步/下一步箭头或面包屑。isBefore(step)/isAfter(step)判断当前步骤位于给定步骤之前 / 之后用于区分已完成步骤isBefore为真即已走过与未到达步骤驱动步骤条的打勾 / 置灰状态。3.5 完整重载签名export declare function useStepperT extends string | number( steps: MaybeRefT[], initialStep?: T, ): UseStepperReturnT, T[], T export declare function useStepperT extends Recordstring, any( steps: MaybeRefT, initialStep?: keyof T, ): UseStepperReturnExcludekeyof T, symbol, T, T[keyof T]两个签名都接受可选的initialStep参数数组形态下initialStep是初始步骤名如useStepper([a, b, c], b)对象形态下initialStep是初始步骤的键keyof T排除symbol不传时默认从第一个步骤开始等价于index初始为 0。类型层面的收益对象形态返回的current被推断为T[keyof T]因此current.value.title这样的访问无需任何类型断言即可通过编译这也是相比手写Recordstring, any状态的最大优势。四、实战在 Vue 组件中驱动向导 UI将useStepper与模板结合即可得到一个完整的向导骨架。以下示例同时展示数组与对象形态的常见渲染模式。4.1 数组形态线性注册向导script setup langts import { computed } from vue import { useStepper } from vueuse/core const { current, isFirst, isLast, goToNext, goToPrevious, isCurrent, isBefore, } useStepper([account, profile, review, done]) const progressPercent computed(() { const total 4 const stepMap: Recordstring, number { account: 0, profile: 1, review: 2, done: 3 } return ((stepMap[current.value] 1) / total) * 100 }) /script template div !-- 步骤条 -- ol li v-forstep in [account, profile, review, done] :keystep :class{ active: isCurrent(step), done: isBefore(step) } {{ step }} /li /ol !-- 当前步骤内容 -- template v-ifcurrent account…账号表单…/template template v-else-ifcurrent profile…资料表单…/template template v-else-ifcurrent review…确认页…/template template v-else…完成页…/template !-- 导航按钮 -- button :disabledisFirst clickgoToPreviousBack/button button clickgoToNext{{ isLast ? Finish : Next }}/button /div /templateisBefore(step)返回true意味着该步骤位于当前步骤之前、已被走过用于把已完成步骤渲染为高亮/打勾状态isLast控制最后一步的按钮文案切换为 Finish。4.2 对象形态携带元数据的设置向导script setup langts import { useStepper } from vueuse/core const stepper useStepper({ welcome: { title: Welcome, description: 介绍页 }, provider: { title: Configure Provider, description: 配置 AI Provider }, speech: { title: Speech Setup, description: 可选配置语音 }, complete: { title: Complete, description: 完成 }, }, welcome) const { current, index, stepNames, goTo, goToNext, goToPrevious, isFirst, isLast } stepper /script template section !-- 头部直接访问对象字段 -- h2{{ current.title }}/h2 p{{ current.description }}/p pStep {{ index 1 }} / {{ stepNames.length }}/p !-- 进度点用 stepNames 渲染 -- div classdots button v-for(name, i) in stepNames :keyname classdot :class{ active: i index, passed: i index } clickgoTo(name) / /div !-- 导航 -- button :disabledisFirst clickgoToPreviousBack/button button clickgoToNext{{ isLast ? Finish : Next }}/button /section /template对象形态下current.title、current.description直接可用stepNames统一给出步骤名的有序数组让进度点这种需要遍历全部步骤的 UI 可以脱离原始对象结构独立渲染。五、仓库佐证stage-ui 中 Steppers 组件的状态设计对照useStepper描述的状态模型与 Airi 仓库packages/stage-ui组件库中的 Steppers 组件高度同构对照阅读可以更直观地理解这套 API 的边界条件设计。5.1 Steppers 组件v-model 下标 边界保护packages/stage-ui/src/components/misc/steppers/steppers.vue 是一个自带进度点、Back/Next 按钮和动效的多步骤容器组件。它的内部状态设计如下const isFirstStep computed(() value.value 0) const isLastStep computed(() value.value props.steps.length - 1) function back() { if (!isFirstStep.value) { value.value-- } } function next() { if (isLastStep.value) { emit(finish) } else { value.value } } function goToStep(index: number) { value.value index }这里isFirstStep/isLastStep边界判定 第一步不能后退、最后一步触发finish而非前进的行为与useStepper的isFirst/isLast/goToNext/goToPrevious设计完全一致——goToNext在最后一步什么都不做、goToPrevious在第一步什么都不做正是为了防止越界而做的语义化封装。组件还通过defineModelnumber暴露v-model下标其状态本质就是一个可写的index。组件使用方式见 steppers.story.vueSteppers v-modelcurrentStepIndex :stepssetupWorkflowSteps step-keyid finishhandleFinish template #header pFirst time? Follow this guide ({{ currentStepIndex 1 }} / {{ setupWorkflowSteps.length }})/p /template /StepperssetupWorkflowSteps即前面提到的 10 步 StageWeb 新手引导配置 Provider → 设置 Consciousness → 可选 TTS → 完成handleFinish在最后一步触发时把currentStepIndex重置回 0对应useStepper场景中向导结束后回到起点的常见收尾逻辑。5.2 Steps Story判定函数的 UI 等价物packages/stage-ui/src/components/misc/steps/steps.story.vue 中的多个步骤条变体手工实现了与useStepper判定函数等价的状态判定function isStepActive(state: { currentStep: Refnumber }, stepId: number): boolean { return state.currentStep.value stepId } function isStepCompleted(state: { currentStep: Refnumber }, stepId: number): boolean { return state.currentStep.value stepId }isStepActive对应isCurrent(step)isStepCompleted对应isBefore(step)。story 中 Accordion Steps 变体据此给已完成步骤打勾、给当前步骤高亮边框Stacked Cards 变体用step.id - currentStep计算卡片堆叠的缩放、位移与模糊。值得注意的还有它的条件跳过逻辑function nextSetupGuideStep() { const current setupGuide.currentStep.value if (current 6 setupGuide.skippedTTS.value) { updateSetupGuideStep(10) // Skip TTS } else if (current setupGuide.totalSteps) { updateSetupGuideStep(current 1) } }用户选择 Skip TTS 后向导从第 6 步直接跳到第 10 步完成页。用useStepper表达同样语义时就是goTo(complete)这样的具名跳转——相比裸下标10步骤名的可读性与可维护性明显更高。这正印证了该 skill 的核心原则Prefer VueUse composables over custom code to improve readability, maintainability, and performance优先使用 VueUse 组合式函数而非自定义代码见 SKILL.md。六、进阶模式与注意事项6.1 条件跳过与分支向导常需根据用户选择跳过某些步骤。useStepper的具名goTo天然适合表达跳过一个区间function next() { if (current.value tts-question skipTTS.value) { goTo(complete) // 跳过 TTS 三步 } else { goToNext() } } function back() { if (current.value complete skipTTS.value) { goTo(tts-question) // 回退到分支决策点 } else { goToPrevious() } }这与 steps.story.vue 中skippedTTSupdateSetupGuideStep(10)的跳过逻辑等价但将魔法数字6/10换成了步骤名逻辑自文档化。6.2 步骤名作为路由/持久化键数组形态的步骤名string | number可以直接作为 URL 参数或存储键// 初始化时从 URL 恢复 const route useRoute() const stepper useStepper(stepNames, route.query.step as string) // 步骤变化时同步到 URL watch(stepper.current, (step) { router.replace({ query: { step } }) })对象形态下的stepNames同样是有序名称列表可复用同一套持久化逻辑而不必关心底层是数组还是对象。6.3 注意事项不要对current直接赋值current是ComputedRef应通过goTo/goToNext/goToPrevious/goBackTo或写入index来切换步骤保证边界保护逻辑生效。goTo传入不存在的步骤名时无效果与goToNext在最后一步静默无操作一致调用前可通过get(step)判空。对象形态的键排除symbol类型签名使用Excludekeyof T, symbol步骤名请使用字符串或数字字面量。适用前提useStepper依赖 Vue 3 的ref/computed响应式体系要求 Vue 3或 Nuxt 3及以上项目Airi 仓库通过 pnpm catalog 统一使用vueuse/core^14.4.0版本能力以该版本为准。七、总结useStepper用 17 个类型安全的成员状态 8 个、查询 2 个、导航 4 个、判定 5 个外加 2 个重载签名完整覆盖了多步骤向导的状态建模数组形态适合线性步骤对象形态适合携带元数据的步骤字典current/index驱动渲染goTo*系列负责导航与边界保护isCurrent/isBefore/isAfter负责步骤条状态判定。仓库内 Steppers 组件 与 Steps story 从侧面印证了这套当前步骤下标 首尾边界 已完成/激活判定 条件跳转模型在真实 UI 中的可行性。在 Airi 的 stage-web、stage-tamagotchi、stage-pocket 等 Vue 应用中实现配置向导、角色创建、部署引导等交互时优先使用useStepper可以显著减少模板化状态代码让向导逻辑更简洁、更可维护。【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考