ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenPencil Vue SDK 页面管理组合式函数 `usePageList`:页面切换、增删改与导航面板构建实战

OpenPencil Vue SDK 页面管理组合式函数 `usePageList`:页面切换、增删改与导航面板构建实战 前端桌面应用AI 应用MCP 服务【免费下载链接】open-pencilAI-native design editor. Open-source Figma alternative.项目地址https://gitcode.com/gh_mirrors/op/open-pencil点击查看免费下载usePageList()是 OpenPencil开源 AI 原生设计编辑器Figma 的开源替代方案Vue SDK 中用于页面管理的核心组合式函数负责驱动页面切换、创建、删除、重命名与排序等操作。本文以 packages/docs/programmable/sdk/api/composables/use-page-list.md 为骨架结合packages/vue与packages/core的真实源码实现系统讲解它的响应式数据、页面动作 API、底层调用链以及如何用它搭建页面切换器、页面列表与导航面板。读完本文你将能够独立实现一个带页面增删、重命名、排序与行内编辑能力的完整页面导航 UI。一、usePageList是什么usePageList()是页面列表 UIpage list UI背后的页面管理组合式函数。在设计编辑器的侧边栏、页面切换器page switcher或页面导航面板中开发者通常需要三样东西当前文档的页面列表数据当前激活页面的标识一组页面操作动作切换、新建、删除、重命名。官方英文文档对其定位的描述是usePageList()is the page-management composable behind page list UIs.页面列表 UI 背后的页面管理组合式函数。也就是说它不是一个通用工具而是专门服务于页面导航类界面的组合式函数——在open-pencil/vue中它属于 组合式函数 API 索引 中Variables, navigation, and localization分组下的导航能力。与其配套的还有两个相关 API官方文档的 Related APIs 部分PageListRoot无头headless的页面列表结构原语提供 slot props 来分发数据与动作useMenuModel菜单模型组合式函数在英文原版文档中作为相关 API 出现法语版本文档还关联了 useInlineRename用于行内重命名场景的编辑状态与焦点管理。二、响应式数据与页面动作完整 API 一览从源码 usePageList.ts 可以看到它的完整实现import { computed } from vue import { useEditor } from #vue/editor/context import { useSceneComputed } from #vue/internal/scene-computed/use export function usePageList() { const editor useEditor() const pages useSceneComputed(() editor.graph.getPages()) const currentPageId computed(() editor.state.currentPageId) return { editor, pages, currentPageId, switchPage: editor.switchPage, addPage: editor.addPage, deletePage: editor.deletePage, movePage: editor.movePage, renamePage: editor.renamePage } }可以看到该组合式函数实际返回的字段比官方文档列出的六个pages、currentPageId、switchPage、addPage、deletePage、renamePage还要丰富完整成员如下成员类型说明editorEditor当前注入的编辑器实例需要时可直接访问底层 APIpagesComputedRefPage[]响应式页面列表由场景图scene graph派生currentPageIdComputedRefstring当前激活页面 ID直接对应editor.state.currentPageIdswitchPage(pageId)(pageId: string, options?) Promisevoid切换当前页面addPage(name?)(name?: string) string新建页面并切换到新页面deletePage(pageId)(pageId: string) void删除指定页面删除当前页时自动切换到邻近页movePage(pageId, index)(pageId: string, index: number) void将页面移动到指定索引重新排序renamePage(pageId, name)(pageId: string, name: string) void重命名页面注意renamePage在法语版文档中被描述为返回正在重命名页面的标识符lidentifiant de la page en cours de renommage但查看实际源码实现usePageList本身并不持有重命名状态行内重命名状态由配套的useInlineRename组合式函数管理后者通过editingId暴露正在编辑的页面 ID。官方文档将两者作为Voir aussi另见关联。导出位置usePageList由open-pencil/vue包名导出入口见 packages/vue/src/index.tsexport { usePageList } from #vue/primitives/PageList/usePageList export { PageListRoot } from #vue/primitives/PageList因此在使用时直接import { usePageList } from open-pencil/vue三、基础用法一行代码拿到页面数据与动作官方英文文档给出的基础用法非常简洁import { usePageList } from open-pencil/vue const pageList usePageList()以及最常用的解构形式const { pages, currentPageId, switchPage, addPage } usePageList()3.1 前置条件必须先provideEditorusePageList内部第一步调用的是useEditor()见源码 packages/vue/src/editor/context/index.ts它通过 Vue 的inject从注入上下文读取编辑器实例export function useEditor(): Editor { const editor inject(EDITOR_KEY) if (!editor) { throw new Error( [open-pencil] useEditor() called without an injected editor. Call provideEditor(editor) near the top of your Vue subtree first. ) } return editor }这意味着任何调用usePageList()的组件必须位于已经调用过provideEditor(editor)的 Vue 子树之下否则会抛出[open-pencil] useEditor() called without an injected editor.错误。规范的做法是在编辑器外壳editor shell组件顶部调用一次provideEditor让所有后代组件包括组合式函数与无头原语都能通过useEditor访问编辑器。3.2pages的响应式原理useSceneComputedpages并不是一个普通computed而是通过useSceneComputed包装的场景派生计算状态。查看其实现 packages/vue/src/internal/scene-computed/use.tsexport function useSceneComputedT(fn: () T): ComputedRefT { const editor useEditor() return computed(() { void editor.state.sceneVersion void editor.state.selectedIds void editor.state.currentPageId return fn() }) }它主动读取sceneVersion、selectedIds、currentPageId三个状态作为响应式依赖因此当场景图版本、选区或当前页面发生变化时pages会自动重新求值——这意味着页面列表会随文档内容变更例如从 .fig 文件导入产生的新页面实时刷新无需手动触发。currentPageId则是直接的computed(() editor.state.currentPageId)与编辑器状态中的当前页面 ID 严格同步。四、实战示例页面操作四件套4.1 切换页面switchPage(pageId)在官方文档中这就是全部所需。切换是异步的返回Promisevoid因为底层需要为页面做准备见下文第五节。在 UI 中通常配合当前页高亮使用button v-forpage in pages :keypage.id :data-activepage.id currentPageId clickswitchPage(page.id) {{ page.name }} /button4.2 新建页面addPage()addPage接受可选的名字参数不传时底层会生成默认名详见第五节。调用后编辑器会立即切换到新页面因此新建页面后用户会直接进入空白新页。4.3 删除与重命名、排序deletePage(pageId) renamePage(pageId, newName) movePage(pageId, targetIndex)这三个动作在英文原版文档中未逐项展开但它们都来自同一组编辑器动作直接透传自editor见usePageList源码的返回语句。语义由 packages/core/src/editor/pages.ts 中的createPageActions定义deletePage(pageId)如果只剩一页则拒绝删除if (pages.length 1) return删除当前页时会自动切换到索引最接近的剩余页renamePage(pageId, name)调用ctx.graph.updateNode(pageId, { name })即修改场景图中页面对应节点的name属性movePage(pageId, index)将目标索引钳制到[0, pages.length - 1]范围再通过ctx.graph.insertChildAt(pageId, ctx.graph.rootId, nextIndex)调整页面在根节点下的顺序。五、底层原理switchPage 的页面准备与提交管线usePageList的各个动作都是对编辑器动作的薄封装真正的核心逻辑在 packages/core/src/editor/pages.ts 的createPageActions(ctx)中。以switchPage为例它的调用链是switchPage(pageId) └─ preparePage(pageId, options) // 页面准备管线 ├─ populatePage(...) // 填充懒加载的 Fig 导入根节点可走 Worker ├─ resolvePageFonts(...) // 收集并加载页面所需字体并发上限 4 ├─ missingGraphFontScripts(...) // 解析回退字体fallback └─ computeAllLayouts(...) // 对页面执行自动布局计算 └─ commitPageSwitch(prepared) // 提交切换 ├─ saveCurrentPageViewport() // 保存当前页视口 ├─ state.currentPageId pageId ├─ setSelectedIds(new Set()) // 清空选区 ├─ restorePageViewport(...) // 恢复目标页视口 ├─ emitEditorEvent(page:changed, ...) └─ requestRender()几个值得注意的实现细节切换是异步且有进度回调节点SwitchPageOptions与PreparePageOptions支持onProgress回调与AbortSignal见 packages/core/src/editor/pages.ts 中的PageSwitchProgress接口进度阶段包括populating-page、resolving-fonts、resolving-fallbacks、layout。这意味着 SDK 消费者可以拿到页面加载进度来驱动 loading UI。并发保护通过pageSwitchGeneration代际计数器如果用户在准备期间连续发起多次切换旧代际的准备结果会被丢弃generation ! pageSwitchGeneration即返回null避免切页竞态导致界面状态错乱。切换完成后清空选区ctx.setSelectedIds(new Set())防止旧页面的选区残留到新页面。字体解析字体加载使用limitAsync(..., MAX_CONCURRENT_FONT_LOADS)常量值为 4限制并发并在finally中调用fontManager.unblockNodes(childIds)与getRenderer()?.invalidateAllPictures()保证渲染图片失效重绘。addPage的底层实现同样值得注意仍在 pages.tsfunction addPage(name?: string) { const pages ctx.graph.getPages() const pageName name ?? Page ${pages.length 1} const page ctx.graph.addPage(pageName) void switchPage(page.id) return page.id }默认页面名是Page NN 为当前页数 1新页面创建后立即触发异步switchPage用户马上进入新页返回新页的 ID可用于后续重命名。六、与 PageListRoot 的配合无头原语 组合式函数官方文档将usePageList与 PageListRoot 互为Related APIs。两者的分工是usePageList面向脚本逻辑——在任意组件中直接拿到数据与动作PageListRoot面向模板结构——一个 headless无头结构原语只渲染一个具名插槽把数据与动作作为 slot props 分发UI 完全由调用方自定义。查看 PageListRoot.vue 的源码它内部就是直接调用usePageList()const { pages, currentPageId, switchPage, addPage, renamePage, deletePage, movePage } usePageList()并把动作包装成统一的actions对象add、switch、rename、delete、move连同数据一起通过 slot 暴露slot :pagespages :current-page-idcurrentPageId :is-dividerisDivider :actionsactions /它还提供了isDivider分隔符检测能力默认分隔符正则/^[-–—*\s]$/即只包含短横线、长横线、星号或空白字符、且没有子节点page.childIds.length 0的页面会被识别为视觉分隔符同时支持通过dividerPatternprop 传入自定义正则。PageListRoot 的官方基础示例PageListRoot v-slot{ pages, currentPageId, switchPage } ul li v-forpage in pages :keypage.id button :data-activepage.id currentPageId clickswitchPage(page.id) {{ page.name }} /button /li /ul /PageListRoot七、实战组合成完整页面导航面板官方 导航面板指南 给出了推荐的组合模式侧边栏顶部放页面导航下方放图层导航LayerTreeRoot行组件内嵌行内重命名控件。一个完整的页面导航区块可以这样写同时使用usePageList与useInlineRenamescript setup langts import { usePageList, useInlineRename } from open-pencil/vue const { pages, currentPageId, switchPage, addPage, deletePage, renamePage, movePage } usePageList() const rename useInlineRename((pageId, newName) { renamePage(pageId, newName) }) /script template div classpage-panel button clickaddPage()New page/button ul li v-for(page, index) in pages :keypage.id button :data-activepage.id currentPageId clickswitchPage(page.id) {{ page.name }} /button button clickrename.start(page.id, page.name)✎/button button clickdeletePage(page.id)×/button button clickmovePage(page.id, index - 1)↑/button button clickmovePage(page.id, index 1)↓/button input v-ifrename.editingId page.id :valuepage.name keydownrename.onKeydown blurrename.commit(page.id, $event) :refrename.focusInput / /li /ul /div /template其中useInlineRename的职责源码见 packages/vue/src/editor/inline-rename/use.tseditingId当前正在编辑的条目 IDstart(id, currentName)进入编辑态并记录原始名字focusInput自动聚焦并全选输入框内容同时注册点击外部onClickOutside自动失焦commit(id, eventOrInput)读取输入值trim 后非空且与原名不同才调用onCommit回调即renamePagecancel()与onKeydown支持Esc取消Enter提交通过blurTarget触发 blur 完成提交。这样就把页面列表的数据层usePageList、交互层useInlineRename、结构层也可改用PageListRoot完整地组合成了一个可投入实际使用的页面导航面板。八、在编辑器命令系统中的实际应用usePageList不止服务于页面导航 UI它还被编辑器的命令系统复用。查看 packages/vue/src/editor/commands/use.tsexport function useEditorCommands() { const editor useEditor() const selection useSelectionState() const capabilities useSelectionCapabilities() const { pages } usePageList() // ... const otherPages computed(() pages.value.filter((page) page.id ! editor.state.currentPageId) ) // 用于 move selection to page 等命令 function moveSelectionToPage(pageId: string) { if (!capabilities.canMoveToPage.value) return editor.moveToPage(pageId) } }也就是说命令系统通过usePageList()拿到全部页面再过滤出其他页面otherPages用于实现将选中图层移动到另一页之类的菜单命令。这印证了该组合式函数的复用价值任何需要页面清单的地方导航 UI、菜单、键盘命令都可以直接消费它而不必在组件里手动读取场景图这正是源码注释中without manually reading the graph in each component的设计意图。九、相关 API 快速导航usePageList本文主角页面数据与动作PageListRootheadless 页面列表结构原语slot props 分发useInlineRename行内重命名状态与焦点管理useEditor / provideEditor编辑器实例的注入与获取usePageList的运行前置条件导航面板指南页面导航 图层导航的组合实践组合式函数总览Composables 索引。十、小结usePageList是 OpenPencil Vue SDK 中一条小而精的导航 API对外只暴露 8 个成员1 个编辑器引用、2 个响应式状态、5 个动作但背后串联起了场景图读取editor.graph.getPages、响应式派生useSceneComputed、编辑器状态同步currentPageId与一整套页面准备/提交管线字体解析、懒加载填充、视口恢复、选区清空、page:changed事件。无论是快速搭建一个页面切换器还是实现完整的页面导航面板配合PageListRoot与useInlineRename它都能让你在不解耦、不重复读图的前提下获得页面管理的全部能力——这是构建 Figma 式页面侧边栏时最值得优先使用的组合式函数之一。赞分享前端桌面应用AI 应用MCP 服务【免费下载链接】open-pencilAI-native design editor. Open-source Figma alternative.项目地址https://gitcode.com/gh_mirrors/op/open-pencil点击查看免费下载相关推荐OpenPencil 页面管理组合式 API usePageList 实战指南构建页面列表与导航面板OpenPencil 页面管理组合式 API usePageList 实战指南构建页面列表与导航面板 usePageList 是 OpenPencil Vue前端桌面应用AI 应用MCP 服务用 PageListRoot 构建无样式的页面导航面板OpenPencil Vue SDK 页面列表演示原语用 PageListRoot 构建无样式的页面导航面板OpenPencil Vue SDK 页面列表演示原语 PageListRoot 是 OpenPenci前端桌面应用AI 应用MCP 服务open-pencil Vue SDK 页面管理实战usePageList 组合式 API 使用与源码解析open pencil Vue SDK 页面管理实战usePageList 组合式 API 使用与源码解析 usePageList 是 open pencil前端桌面应用AI 应用MCP 服务上一篇深入理解hb_build架构从源码角度看OpenHarmony构建机制下一篇使用 Turf transformTranslate 平移 GeoJSON 几何体Rhumb 线方向移动的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表