尧图精选

Bytebase SQL Editor React 迁移 Stage 7:SharePopover 组件迁移的设计与落地全解析

🕒 发布时间:2026/9/15 17:17:54 📁 来源:尧图网络
Bytebase SQL Editor React 迁移 Stage 7SharePopover 组件迁移的设计与落地全解析【免费下载链接】bytebaseDatabase governance built for humans and agents — controlling changes and access across every major database.项目地址: https://gitcode.com/GitHub_Trending/by/bytebase本篇文章围绕 Bytebase 前端仓库中 SQL Editor React 迁移第 7 阶段Stage 7的设计文档展开完整还原了将 Vue 组件EditorCommon/SharePopover.vue迁移为 React 组件SharePopoverBody的架构决策、调用方替换步骤、i18n 与验证方案。文档本身定义于 2026-04-21-sql-editor-react-migration-stage-7-design.md当前仓库中该阶段的实际实现已经落地于frontend/src/modules/sql-editor/components/本文将以设计稿为主线、以仓库现状为佐证帮助你掌握 Bytebase 在 Vue 2/React 并存期内以ReactPageMount桥接、单组件逐块迁移的工程方法论并可直接照搬到同仓库后续任何 Vue 组件的迁移任务中。1. 阶段目标与非目标Goal non-goalsStage 7 的核心目标是将EditorCommon/SharePopover.vue228 行、2 个 Vue 调用方整体迁移为 React 组件。设计文档要求两个 Vue 调用方EditorAction.vue与SheetTree.vue统一切换到ReactPageMount pageSharePopoverBody ... /挂载方式并复用 Stage 6 构建的Popover基础组件来实现弹层内的嵌套可见性选择器。这是迁移中一个关键的里程碑——它是第一个多调用方迁移、且迁移完成后 Vue 源文件可被干净删除的案例为后续多调用方组件的清理树立了先例。文档同时明确划定了非目标Non-goals避免迁移范围失控不移植CopyButton.vue该组件有 10 个以上 Vue 调用方属于独立话题React 侧直接采用多文件中已确立的navigator.clipboard.writeText内联模式不做 Emittery 或异步动作桥接推迟到后续阶段不迁移SheetTree.vue851 行或EditorAction.vue本身只替换它们内部嵌入SharePopover的那一行不移动外层NPopover的点击触发器工具栏上的 Share 按钮只迁移 popover 的内容体不弃用 CopyButton它仍在其他大量场景被使用。这一目标精确、边界清晰的拆解方式是整套迁移能够持续推进的关键每次只替换一个组件的内容体外层容器与触发器保持不变风险被控制在单点。2. 架构一个 React 组件替换 Vue Popover 的内容插槽设计的核心架构决策是创建一个 React 组件SharePopoverBody.tsx约 200 行替换 VueSharePopover的组件体并作为 Vue 外层NPopover的内容插槽被挂载。两个调用方使用完全相同的模式!-- BeforeEditorAction 与 SheetTree 均使用此模式 -- NPopover triggerclick ... template #trigger...share button.../template template #default SharePopover :worksheet... on-updated... / /template /NPopover !-- After -- NPopover triggerclick ... template #trigger...share button.../template template #default ReactPageMount pageSharePopoverBody :worksheet... :onUpdated... / /template /NPopover注意其中的一个关键细节on-updatedVue 事件 emit要改写成:onUpdated显式 prop 回调。这是 Stage 1 沉淀下来的 kebab-case prop 经验——Vue 的attrs不会自动将 kebab-case 绑定 camelCase 化因此跨框架传 prop 时必须显式使用驼峰命名ReactPageMount才能将其正确传给 React 侧组件。从当前仓库现状看ReactPageMount这种Vue 调用方 React 内容组件的桥接模式已在 SQL Editor 中广泛铺开frontend/src/modules/sql-editor/components/下存在大量顶层页面 shim如 SQLEditorHomePage.tsx、ResultPanelPage.tsx、PanelsPage.tsx 等其文件头注释均明确写着Vue caller mounts this withReactPageMount page... /。这印证了 Stage 7 的设计并非孤例而是整套迁移体系中的标准动作。3. React 组件设计Props、结构、Stores 与交互流程设计文档对SharePopoverBody.tsx给出了精确的规格定义。3.1 Props 契约type Props { readonly worksheet?: Worksheet; readonly onUpdated?: () void; };worksheet为可选分享对象onUpdated为显式 prop 回调用于替代 Vue 侧的on-updated事件。3.2 组件结构镜像 Vue 源码组件体分为两大区块头部区块Share 标题 可见性选择器。可见性选择器是一个嵌套 Popover复用 Stage 6 的Popover基础组件选择器触发器显示当前访问级别文案 ChevronDown图标嵌套 popover 内容列出 3 个选项Private、Project Read、Project Write每个选项包含图标Private 用LockKeyhole两个 Project 选项用Users 标签 描述 当前选中项的Check标记非创建者看到的可见性选择器呈只读disabled样式。链接展示区块展示可分享 URL 的输入框前缀Link2图标 复制按钮。复制按钮直接调用navigator.clipboard.writeText不复用 CopyButton 组件复制按钮在当前 tab 状态不是CLEAN时禁用镜像 Vue 侧的判断谓词。3.3 顶层 Stores 与 Router设计文档指定了组件需要接入的全局状态与路由用途Store / API说明workspaceExternalURLuseActuatorV1Store用于拼装绝对分享链接me创建者校验useCurrentUserV1判断当前用户是否为创建者patchWorksheetuseWorkSheetStore持久化可见性变更成功 toastpushNotification自/store导入复制/更新成功提示currentTabuseSQLEditorTabStore复制按钮禁用门控Router 部分采用如下方式构建分享链接router.resolve({ name: SQL_EDITOR_WORKSHEET_MODULE, params: { project, sheet }, }).href // 再通过 new URL(href, workspaceExternalURL || window.location.origin) 拼出绝对 URL即先用 Vue Router 的resolve得到相对 href再以工作区外部 URL缺省回退到window.location.origin为基础构造绝对链接。3.4 图标替换清单heroicons → lucide-reactheroicons-solidchevron-down→ lucide-react 的ChevronDownheroicons-solidcheck→Checkheroicons-solidlink→Link2LockKeyhole、Users—— 与 Vue 侧同名Vue 也使用 lucide-vue-next3.5 访问变更流程handleChangeAccess设计文档给出了 5 步精确流程守卫用户不是创建者或没有 worksheet → 直接 no-op持久化调用worksheetStore.patchWorksheet(worksheet, { visibility }, [visibility])第三个参数指定仅更新visibility字段剪贴板若剪贴板可用调用navigator.clipboard.writeText(sharedTabLink)并推送通知 URL copied to clipboard否则推送 Updated回调调用onUpdated?.()prop 回调对应 Vue 源码的on-updated事件收尾关闭嵌套的可见性选择器 popover。4. Vue 调用方替换Caller Swaps4.1EditorAction.vue定位当前文件中SharePopover :worksheet... /出现处文档标注约在文件第 107 行做如下替换!-- 替换前 -- SharePopover :worksheetsheetAndTabStore.currentSheet / !-- 替换后 -- ReactPageMount pageSharePopoverBody :worksheetsheetAndTabStore.currentSheet /同时删除import SharePopover from ./SharePopover.vue;这一行。ReactPageMount在 Stage 4 已导入无需重复添加。由于 Vue 原组件本就没有传onUpdated此处也无需补传。4.2SheetTree.vue先用 grep 定位调用点grep -n SharePopover frontend/src/views/sql-editor/AsidePanel/WorksheetPane/SheetList/SheetTree.vue然后替换!-- 替换前 -- SharePopover :worksheetworksheetEntity on-updatedhandleContextMenuClickOutside / !-- 替换后 -- ReactPageMount pageSharePopoverBody :worksheetworksheetEntity :onUpdatedhandleContextMenuClickOutside /同时删除import SharePopover from /views/sql-editor/EditorCommon/SharePopover.vue;若import ReactPageMount from /react/ReactPageMount.vue;尚不存在则补上。此处的:onUpdated即第 2 节提到的事件 emit → prop 回调改写。5. Vue 文件删除多调用方迁移的清理先例两个调用方同步迁移后Vue 源文件不再有任何引用文档给出了严格的删除前验证命令grep -rn SharePopover frontend/src/ | grep -v frontend/src/react/预期输出除了被删除文件本身和EditorCommon/index.tsbarrel 之外零 Vue 引用。确认后执行rm frontend/src/views/sql-editor/EditorCommon/SharePopover.vue并同步将SharePopover从frontend/src/views/sql-editor/EditorCommon/index.ts的 import 块与 export 块中移除。文档特别强调这是第一个多调用方迁移、且迁移后 Vue 文件可以干净删除的案例因为两个调用方一起迁移完毕。此前单调用方迁移往往因剩余调用方而无法删除 Vue 文件该阶段确立了多调用方全量替换 → 验证零引用 → 删除 清理 barrel的标准清理流程为后续多调用方组件的清理工作提供了先例。6. i18n 键管理文档要求在所有5 个 React locale 文件中核对以下 i18n 键缺失时**逐字节byte-exact**复用 Vue locale 中的既有值补齐sql-editor.link-accesssql-editor.privatesql-editor.private-descsql-editor.project-readsql-editor.project-read-descsql-editor.project-writesql-editor.project-write-descsql-editor.url-copied-to-clipboardcommon.share大概率已存在common.updated大概率已存在从仓库现状看5 个 locale 文件确实齐备frontend/src/locales/下存在en-US.json、es-ES.json、ja-JP.json、vi-VN.json、zh-CN.json且迁移后的 React 组件中确实通过useTranslation()的t()引用了common.share与sql-editor.url-copied-to-clipboard等键——5 个 React locale 全量同步的要求在落地阶段被严格执行。7. 验证方案自动化测试 手动 UX7.1 自动化验证文档要求全量检查命令通过pnpm fix check type-check test并在SharePopoverBody.test.tsx中新增约6 个测试渲染 Share 标题与链接输入框展示 3 个可见性选项Private、Project Read、Project Write且图标正确非创建者场景下可见性选择器呈禁用态handleChangeAccess会调用patchWorksheet、pushNotification并触发onUpdated回调复制按钮写入剪贴板并推送通知currentTab?.status ! CLEAN时复制按钮禁用。仓库现状印证了测试体系确实落地frontend/src/modules/sql-editor/components/SharePopoverBody.test.tsx中实际存在渲染 Share 标题与链接输入框复制按钮写入剪贴板并推送通知无分享链接时复制按钮禁用等用例测试通过vi.hoisted统一 mock 了react-i18next、/stores/app、/utils与/app/router并显式设置了IS_REACT_ACT_ENVIRONMENT以在createRoot下用act()驱动渲染与断言。7.2 手动 UX 验收清单EditorAction 分享按钮点击打开外层 popover → 应看到 Share 标题 可见性选择器 URL 复制按钮复制按钮点击URL 被复制 toast 通知弹出修改可见性仅创建者worksheet 更新、URL 自动复制、toast 弹出非创建者可见性选择器呈禁用态SheetTree 右键菜单分享点击打开 popover → 行为同上且触发onUpdated后父级菜单关闭。8. 延后项Out of scope文档明确列出的延后项均属于独立或跨阶段工作CopyButton 的 React 移植10 Vue 调用方独立关注点Emittery 事件桥接异步动作桥接如maybeSwitchProject/createWorksheet/abortAutoSave等Vue-in-React 挂载基础设施。这些项被推迟而非忽略为后续阶段保留了清晰的演进路径。9. 实践检查清单设计文档末尾提供了一份可直接勾选的落地清单概括了 Stage 7 的全部交付物创建react/components/sql-editor/SharePopoverBody.tsx及测试核对/补齐 8 个 i18n 键EditorAction.vue替换1 行模板 1 行 importSheetTree.vue替换1 行模板 1 行 import删除 VueSharePopover.vue移除EditorCommon/index.ts中的 barrel import 与 exportpnpm fix check type-check test全绿从两个调用点EditorAction 分享按钮 SheetTree 右键菜单分享完成手动 UX 验证10. 从设计到落地仓库中的实际实现与演进设计文档以 Draft 状态落笔而当前仓库中该阶段的实际实现已经完成并继续演进二者的对照本身就是一份极具价值的设计与现实教材。实际组件位于 SharePopoverBody.tsx文件头注释明确写着Replaces frontend/src/views/sql-editor/EditorCommon/SharePopover.vue与设计文档目标一一对应。主要演进点如下术语演进设计稿使用worksheet落地实现使用savedQuery类型为SavedQuery——仓库的数据模型已从 worksheet 术语迁移到 saved query 术语路由 handle 为SQL_EDITOR_SAVED_QUERY_MODULEfrontend/src/app/router/handles中可查Store 收敛设计稿的useActuatorV1Store/useCurrentUserV1/useWorkSheetStore/useSQLEditorTabStore在落地实现中收敛为单一 useAppStore读取serverInfo?.externalUrl与currentUser并通过useAppStore.getState().notify({...})完成成功通知复制门控的细化设计稿按currentTab?.status ! CLEAN禁用复制按钮落地实现改为按分享对象是否具有链接disabled{!sharedTabLink}门控——组件注释解释了原因popover 可从树中为任意 saved query 打开当前 tab 的脏状态与此无关只有未保存草稿无 savedQuery → 无链接才应禁用剪贴板工具的健壮化设计稿要求内联navigator.clipboard.writeText落地实现抽出了 writeTextToClipboard 工具函数先尝试navigator.clipboard.writeText失败或不可用时回退到隐藏textareadocument.execCommand(copy)的旧式方案兼容非安全上下文与旧浏览器访问模型升级设计稿的 3 档可见性选择器Private / Project Read / Project Write在落地实现中升级为 SavedQueryGrantEditor 授权编辑器——分享语义从可见性档位演化为基于 IAM policy 的细粒度授权创建者或具备项目级bb.savedQueries.setIamPolicy权限者通过isSavedQueryShareableV1判定可管理授权且链接只承载位置、授权才承载访问的职责分离在组件注释中被明确记录。结语Stage 7 的设计文档完整展示了 Bytebase SQL Editor React 迁移在单组件粒度上的标准作业精确的目标/非目标界定、ReactPageMount桥接模式、事件到 prop 回调的改写规则、多调用方同步替换后干净删除 Vue 文件的清理先例以及覆盖自动化测试与手动 UX 的双重验证体系。仓库中已经落地的实现进一步印证了这套方法论的可持续性——组件可以在保持架构骨架不变的前提下随数据模型与访问模型演进而重构而迁移节奏本身不受影响。对于需要在本仓库内推进 Vue→React 渐进式迁移的读者本文所述的每一个环节设计文档、落地组件、测试用例、剪贴板工具都可以直接作为下一阶段迁移的参照模板。【免费下载链接】bytebaseDatabase governance built for humans and agents — controlling changes and access across every major database.项目地址: https://gitcode.com/GitHub_Trending/by/bytebase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →