尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

深入解析 wp-calypso Stepper 的 Onboarding Sessions:会话机制、持久化原理与 Checkout 回退实践

深入解析 wp-calypso Stepper 的 Onboarding Sessions:会话机制、持久化原理与 Checkout 回退实践 前端CMS【免费下载链接】wp-calypsoThe JavaScript and API powered WordPress.com项目地址https://gitcode.com/gh_mirrors/wp/wp-calypso点击查看免费下载导读本文聚焦 WordPress.com 前端单体仓库 wp-calypso 中client/landing/stepper模块的 Onboarding Sessions会话机制系统讲解 Session 的概念、~XX格式 Session ID 的生成算法、基于 React Query IndexedDB 的状态持久化链路以及“从 Checkout 返回修改选择”这一关键能力的实现原理。读完本文你将理解 Stepper 引导流程如何在刷新、多开标签页、中途放弃后依然保持用户进度并能在自己的 flow 中正确开启会话能力__experimentalUseSessions与使用useFlowState读写会话状态。什么是 Onboarding Session在 Stepper flow引导式建站流程的语境中Session 代表用户一次完整的“开始并走完某个 flow”的尝试。每一次尝试都是独立且唯一的Session 内部承载了该次流程体验的全部状态与进度sessions.md 明确列出了 Session 需要支撑的关键用户场景连续创建多个站点每次建站都是一次独立 Session互不干扰刷新页面不丢进度刷新后从 IndexedDB 恢复状态用户无需重走流程新标签页打开链接保持上下文URL 中的 sessionId 随链接传播新标签页能重建同一个上下文中途放弃后回来继续Session 在浏览器存储中长期存活可被重新拾取从 Checkout 返回修改选择站点已创建、但用户反悔想换套餐或域名时可以安全返回。从源码结构看Stepper 的入口 index.tsx 会在启动时读取或生成 sessionId并将其写入 URL再基于它初始化 React Query 的持久化上下文——整个会话体系由此串联起来详见下文。Session ID~XX短码的生成与作用格式与用途Session ID 是一个唯一标识符以查询参数的形式挂在引导流程的 URL 上格式为~XX其中X是 base62数字 大小写字母字符。例如/setup/onboarding?sessionId~A0它在整个系统中承担三重职责持久化Persistence作为浏览器存储中的键用于定位该次流程的状态导航Navigation用户在不同步骤间跳转、甚至跨越到 Checkout 应用时靠它维持上下文隔离Isolation多次流程尝试各自拥有独立的 Session ID防止状态串扰。特征Session ID 具有以下生命周期特征对每次流程尝试唯一仅 2 个字符保持 URL 整洁新流程开始时自动生成页面刷新与导航期间保留开启新流程或放弃当前流程时丢失。生成算法base62 双字符编码Session ID 的生成逻辑位于 create-session-id.ts其实现非常精巧const BASE62_ALPHABET 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; function base10ToBase62( num: number ) { let result ; while ( num 0 ) { result BASE62_ALPHABET[ num % 62 ] result; num Math.floor( num / 62 ); } return result || 0; } export function createSessionId() { const minNumberForTwoLettersBase62 62; const maxNumberForTwoLettersBase62 3844; const seed minNumberForTwoLettersBase62 Math.floor( Math.random() * maxNumberForTwoLettersBase62 ); return base10ToBase62( seed ); }源码注释解释了两个设计取舍双字符长度62到3844的随机整数经 base62 编码后恰好得到 2 个字符。两字符足够短不会让 URL 变丑也不会偶然拼出冒犯性单词同时两字符的编码空间足够容纳3782 种枚举3844 - 62在单次流程尝试的场景下冲突概率可忽略读取方式配套的 use-session-id.ts 提供getSessionId()工具从window.location.search也可显式传入 search 字符串中通过URLSearchParams.get( sessionId )提取当前会话 ID读取不到时返回null。会话的启动与绑定__experimentalUseSessionsSession 并非所有 flow 默认启用它是一个显式的 opt-in 能力。在 index.tsx 的启动逻辑中if ( __experimentalUseSessions in flow ) { const sessionId getSessionId() || createSessionId(); history.replaceState( null, , addQueryArgs( { sessionId }, window.location.href ) ); queryClient ( await createQueryClient( stepper-persistence-session- sessionId ) ) .queryClient; } else { queryClient ( await createQueryClient( userId ) ).queryClient; }关键行为若 flow 声明了__experimentalUseSessions标志则先尝试从 URL 读取既有 sessionId用于刷新、返回、新标签页等场景读不到才调用createSessionId()新建生成后通过history.replaceState将 sessionId 回写到 URL不产生新的历史记录因此刷新页面时 sessionId 不会丢失随后以stepper-persistence-session-${sessionId}作为持久化键创建独立的 React Query Client未启用会话的 flow 则退化为按userId键控的全局 QueryClient。在 FlowV2 类型定义 中__experimentalUseSessions?: boolean是可选标志README.md 明确提示若要在 flow 中使用useFlowStatehook必须将__experimentalUseSessions设为true。当前仓库中已启用该标志的 flow 包括00-example-flow、flex-site、plan-upgrade、site-migration-flow、site-setup-flow、woo-hosted-plans 等。状态如何持久化React Query IndexedDB 链路存储键query-state-${sessionId}Session 的持久化建立在 React Query 的持久化能力之上底层存储为 IndexedDB。核心实现在 query-client.ts 的hydrateBrowserState中if ( shouldPersist() ) { const storeKey query-state-${ persistenceKey ?? logged-out }; const persister { persistClient: throttle( ( ( state: PersistedClient ) { state.clientState.queries.forEach( ( query ) { if ( typeof query.meta?.persist function ) { query.meta.persist query.meta.persist( query.state.data ); } } ); return storePersistedStateItem( storeKey, state ); } ) as ( ...args: unknown[] ) unknown, SERIALIZE_THROTTLE, { leading: false, trailing: true } ) as DebouncedFunc ( state: PersistedClient ) Promise void , restoreClient: () getPersistedStateItem( storeKey ), removeClient: () { // not implemented }, }; const [ unsubscribePersister, restorePromise ] persistQueryClient( { queryClient, persister, maxAge: MAX_AGE, dehydrateOptions: { shouldDehydrateQuery, }, } ); await restorePromise; ... }结合上文启动逻辑可知当 flow 启用会话时传入的persistenceKey为stepper-persistence-session-${sessionId}因此实际存储键为query-state-stepper-persistence-session-~A0这样的形态未登录场景则为query-state-logged-out。读写与清理策略写入storePersistedStateItem将序列化后的整个 QueryClient 状态写入存储同时维护内存缓存见 persisted-state.js 的storePersistedStateItem/getPersistedStateItem节流persistClient使用wordpress/compose的throttle包裹SERIALIZE_THROTTLE 5000定义于 constants.ts即写入被节流为最多每 5 秒一次且取 trailing 边缘避免频繁变更时产生大量 IndexedDB 写入恢复restoreClient从存储读取对应键并hydrate回 QueryClient这是刷新/返回后状态得以还原的关键过期回收persistQueryClient的maxAge取MAX_AGE 7 * DAY_IN_HOURS * HOUR_IN_MS即7 天见 constants.ts超过最大年龄的陈旧会话由 React Query 自动垃圾回收卸载兜底createQueryClient注册beforeunload事件在页面卸载前flush()掉节流队列中最后一次待写入的 persist 调用保证刷新瞬间的进度也不丢失降级存储底层 browser-storage/index.ts 会先检测 IndexedDB 是否可用supportsIDB不可用如隐私模式、Safari 受影响版本时自动降级到localStorage并对QuotaExceededError做了一次性禁用 IDB 的处理。useFlowState读写会话状态的类型安全 APIuseFlowState是 flow 开发者与 Session 状态交互的主要入口实现在 store.tsconst PREFIX stepper-state-item; const VERSION v1;其查询键为[PREFIX, flow, session, VERSION]——即同时以 flow 名和 sessionId 作为作用域这正是“同一用户不同流程尝试互不干扰”的隔离保证。源码注释特别说明该状态“没有后端可同步”因此查询配置为const PERSISTENCE_CONFIG { staleTime: Infinity, refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, networkMode: always, } as const;即永不自动重新请求、离线也照常可用数据只在自身set时通过queryClient.setQueryData变更。useFlowState返回{ get, set, sessionId }三个能力且是类型安全的状态的字段类型由 stepper-state-manifest.ts 中的FlowStateManifest定义由“各 step 的submits类型”联合而成外加flow.entryPoint、site、createdSite记录已创建站点的siteId/siteSlug/requestedName等杂项字段见 types.ts。在 flow 中使用以示例 flow 00-example-flow/example.ts 为参考典型用法是在useStepNavigation中获取set在 step 提交时把数据写入会话状态const { get, set } useFlowState(); const submit: SubmitHandler typeof initialize ( submittedStep ) { const { slug, providedDependencies } submittedStep; switch ( slug ) { case newsletterSetup: set( newsletterSetup, providedDependencies ); return navigate( newsletterGoals ); case newsletterGoals: set( newsletterGoals, providedDependencies ); return navigate( domains ); case domains: set( domains, providedDependencies ); return navigate( plans ); case plans: set( plans, providedDependencies ); ... } };后续步骤如processing则通过get( site )读取此前useCreateSite存入的站点信息见 example.ts#L115-L118。这种“step 提交即写入、后续步骤按需读取”的模式配合 IndexedDB 持久化就构成了刷新不丢进度的完整闭环。从 Checkout 返回修改选择Session 的跨应用能力Session 体系最具特色的一项能力是允许用户从 Checkout 返回修改选择。其工作流程如下用户走到 Checkout 时站点已被创建通过useCreateSite等机制同时 sessionId 保留在浏览器的历史栈中——因为启动时是用history.replaceState写入 URL 的而后续流程内的导航都属于同一历史记录链若用户点击返回或重新回到 flowURL 中的 sessionId 让系统可以检索此前创建的站点createdSite字段记录了siteId/siteSlug再次走到 create-site 步骤时会采用已建站点而非向/sites/new再要一个新站点这一设计意图在 stepper-state-manifest.ts#L14-L28 的注释中有明确说明加载全部历史选择与状态从 IndexedDB 中按query-state-stepper-persistence-session-${sessionId}恢复整个 React Query 状态允许修改套餐、域名或其他选择重走相关 step 并覆盖set进去的新值全程维持上下文即使跨入了 Checkout 这一独立应用sessionId 依然有效。这一能力之所以可行本质在于sessionId 作为跨应用边界的“连接键”它随 URL 在 flow 与 checkout 之间传递持久化在浏览器存储中因此系统能随时把用户“接回”上一次的状态。对用户的价值是可以随时改变关于套餐或域名的决定系统不会忘记已创建的站点所有之前的选择都被保留。结语Onboarding Sessions 是 wp-calypso Stepper 引导体系的状态基石~XX双字符 base62 短码在 URL 中扮演持久化键、导航上下文与隔离标识三重角色React Query 的persistQueryClient配合 IndexedDB含 localStorage 降级实现 7 天有效期的自动持久化与 5 秒节流写入useFlowState提供类型安全、按[flow, session]作用域的读写 API而这一切最终支撑起“创建站点后从 Checkout 返回修改选择”这类复杂交互。对希望构建新 flow 的开发者而言只需在 flow 定义中加入__experimentalUseSessions: true并在useStepNavigation中通过useFlowState读写状态即可完整获得会话能力。赞分享前端CMS【免费下载链接】wp-calypsoThe JavaScript and API powered WordPress.com项目地址https://gitcode.com/gh_mirrors/wp/wp-calypso点击查看免费下载相关推荐Yeti Button 组件完全指南用>Yeti Button 组件完全指南用 data variant、data emphasis、data size 构建原生状态驱动的按钮 Yeti READ前端Raveberry WiFi热点功能详解打造移动音乐派对网络Raveberry WiFi热点功能详解打造移动音乐派对网络 Raveberry是一款专注于多人参与的音乐服务器其WiFi热点功能让你轻松打造移动音乐派对网gbrain Agent Memory 解析跨会话事实持久化与召回的工程实践gbrain Agent Memory 解析跨会话事实持久化与召回的工程实践 Agent Memory智能体记忆是 gbrain 中一个核心概念 跨会话人工智能RAGAgent 记忆MCP 服务知识管理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表