2026/9/12 4:31:23

GrapesJS 页面对象 API 详解:Page 的标识、命名与 Frame/组件访问

GrapesJS 页面对象 API 详解:Page 的标识、命名与 Frame/组件访问 GrapesJS 页面对象 API 详解Page 的标识、命名与 Frame/组件访问【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs本文围绕 GrapesJS 官方 API 文档 docs/api/page.md 中的Page对象方法getId、getName、setName、getAllFrames、getMainFrame、getMainComponent展开结合 Page.ts 源码实现与 Pages 模块测试 逐一拆解其签名、返回值与底层行为。读完本文你将掌握在 GrapesJS 多页面项目中获取页面标识、读写页面名称、遍历页面 Frame画布框架以及访问根组件wrapper的完整实战能力并能据此自行构建自定义的 Page Manager 界面。Page 对象从哪来先认识 Pages 模块Page是 GrapesJS 多页面Multi-Page能力的核心数据模型但单独讨论它之前需要先明确一个前提Page 实例通常不会手动new出来而是由editor.PagesPageManager 模块统一创建和管理。要拿到 Page 对象一般有以下几种途径const pages editor.Pages; // 1. 获取全部页面数组 const allPages pages.getAll(); // 2. 获取当前选中的页面 const selectedPage pages.getSelected(); // 3. 按 id 获取指定页面 const somePage pages.get(my-page-id); // 4. 获取主页面第一个页面 const mainPage pages.getMain();从源码看PageManager 的getAll()直接返回[...this.all.models]getSelected()读取内部模型this.model.get(selected)而getMain()优先返回type main的页面否则回退到第一个页面见 packages/core/src/pages/index.ts。值得强调的是即使你的项目不需要多页面GrapesJS 也会在初始化时默认创建一页。测试用例Has by default one page created与The default page is selected分别验证了「默认只有 1 个页面」和「默认页面即选中页面」这两个行为见 packages/core/test/specs/pages/index.ts这保证了 API 的一致性也让你后续扩展多页时无需改动既有逻辑。Page 的属性与初始化配置在深入方法之前先看 Page 支持哪些属性。源码中PageProperties接口定义了以下可配置项见 Page.ts属性类型说明idstring页面唯一标识未显式指定时由模块自动生成随机 idnamestring页面名称默认值为componentstring \| ComponentDefinition \| ComponentDefinition[]页面内容可以是 HTML 字符串或组件 JSON 定义stylesstring \| CssRuleJSON[]随页面加载的 CSS可以是样式字符串或规则 JSON 数组framesFrameProperties[]随页面加载的 Frame 列表多画布框架场景skipFromStorageboolean为true时该页面不参与项目存储对应 PageManager 的store()会过滤掉这些页面这些属性在编辑器初始化时通过pageManager.pages数组传入const editor grapesjs.init({ // ... pageManager: { pages: [ { id: my-first-page, name: 首页, styles: .my-page1-el { color: red }, component: div classmy-page1-elPage 1/div, }, { id: my-second-page, name: 关于我们, component: div classmy-page2-elPage 2/div, }, ], }, });注意一个容易踩坑的细节编辑器顶层的style/components配置与页面配置中的styles/component是不同的键名单复数差异。官方模块指南 docs/modules/Pages.md 中明确警告了这一差异。事实上顶层配置在初始化时会被自动迁移为pageManager.pages中第一个页面的styles与component。从构造函数看Page 在实例化时还会完成几件关键工作见 Page.ts若没有传入frames则自动把component和styles包装成一个默认 Frame并unset掉这两个字段创建Frames集合new Frames(em.Canvas, frms)并与当前 Page 互相关联若无id调用em.Pages._createId()自动生成唯一 id将frames集合注册进 UndoManager使页面增删可以撤销/重做。getId获取页面 id/** * Get page id * returns {String} */ getId() { return this.id as string; }getId()直接返回 Page 的id属性字符串。id 是页面的唯一标识也是 PageManager 中get(id)、remove(pageOrId)、select(pageOrId)等方法的寻址依据——这些方法都支持「传 id 字符串」或「传 Page 实例」两种写法。因此在自定义页面管理 UI 中id 通常被用作列表项的 key 与操作参数。getName获取页面名称/** * Get page name * returns {String} */ getName() { return this.get(name)!; }getName()返回name属性的值。名称与 id 的定位不同id 面向机器唯一标识、存储寻址name面向用户界面展示。默认值为空字符串见 Page.ts 中defaults()的定义实际使用时通常会为每个页面设置一个有语义的名称例如官方模块指南中name: Page 1的写法。setName更新页面名称/** * Update page name * param {String} name New page name * example * page.setName(New name); */ setName(name: string) { return this.set({ name }); }setName(name)接收一个字符串参数作为新名称内部调用this.set({ name })完成更新。由于 Page 继承自 Backbone 风格的Modelset会触发change:name变更事件并冒泡为 Pages 模块的page:update/page事件因此通过setName改名可以无缝联动自定义 UI 的刷新详见下文事件部分。const page editor.Pages.getSelected(); page.setName(新的页面名称); console.log(page.getName()); // 新的页面名称getAllFrames获取页面全部 Frame/** * Get all frames * returns {ArrayFrame} * example * const arrayOfFrames page.getAllFrames(); */ getAllFrames(): Frame[] { return this.getFrames().models || []; }getAllFrames()返回页面对应的全部 Frame 实例数组。Frame 是画布中的「框架」一个页面默认包含一个Frame测试The default page has one frame验证了这一点见 packages/core/test/specs/pages/index.ts但通过frames属性可以为一个页面配置多个 Frame实现同一页面内的多画布/多视口布局。从 Frame.ts 的FrameProperties可以看出每个 Frame 支持以下属性属性类型说明idstringFrame 唯一标识componentstring \| ComponentDefinition \| ComponentDefinition[] \| ComponentFrame 根组件wrapper定义width/heightstring \| number \| null宽高默认取画布尺寸x/ynumber在画布中的水平/垂直位置默认0head{ tag, attributes }[]附加到 Frame 的head标签列表stylesstring \| CssRuleJSON[]Frame 级样式refFrame/refComponentstring \| Frame \| Component \| null引用其他 Frame/组件符号复用场景skipFromStorageboolean是否跳过存储const frames page.getAllFrames(); frames.forEach(frame { console.log(frame.id, frame.getComponent()); });getMainFrame获取主 Frame/** * Get the first frame of the page (identified always as the main one) * returns {Frame} * example * const mainFrame page.getMainFrame(); */ getMainFrame(): Frame { return this.getFrames().at(0); }getMainFrame()返回页面的第一个Frame官方注释明确将其标识为「始终是主框架」。在绝大多数单页单 Frame 场景下它等价于getAllFrames()[0]是一个更语义化、更安全的快捷方法——当你明确只需要「主画布」时不必关心 Frame 集合的长度。拿到 Frame 后可以继续访问 Frame 层级的 API例如getComponent()根组件、getStyles()样式、getBoxRect()位置与尺寸包围盒等均定义于 Frame.ts。getMainComponent获取根组件/** * Get the root component (usually is the wrapper component) from the main frame * returns {Component} * example * const rootComponent page.getMainComponent(); * console.log(rootComponent.toHTML()); */ getMainComponent(): ComponentWrapper { const frame this.getMainFrame(); return frame?.getComponent(); }getMainComponent()是日常开发中使用频率最高的方法之一。它的实现是先取主 Frame再调用frame.getComponent()返回的通常是wrapper 组件——即整个页面内容的根容器测试The default frame has the wrapper component验证了默认 Frame 的根组件类型为wrapper。拿到根组件后就能像操作普通组件树一样遍历、查找、修改整页内容const rootComponent page.getMainComponent(); // 输出页面 HTML console.log(rootComponent.toHTML()); // 查找页面上所有 image 组件 const images rootComponent.findType(image); // 通过编辑器 API 导出指定组件的 HTML/CSS const htmlPage editor.getHtml({ component: rootComponent }); const cssPage editor.getCss({ component: rootComponent });官方模块指南 docs/modules/Pages.md 中给出的程序化用法示例正是利用这一组合先page.getMainComponent()拿到根组件再借助editor.getHtml({ component })与editor.getCss({ component })导出整页代码。此外PageManager 还提供了getAllWrappers()它会跨所有页面、所有 Frame去重收集每个 Frame 的根组件适合全项目级的组件检索// 从整个项目所有页面中收集所有 image 组件 const allImages editor.Pages.getAllWrappers() .map(wrp wrp.findType(image)) .flat();与事件系统联动构建自定义 Page Manager UIGrapesJS 不提供默认的页面管理界面官方推荐通过订阅事件来驱动自定义 UI见 docs/modules/Pages.md 的 Customization 章节。Pages 模块在 packages/core/src/pages/types.ts 中定义了完整事件集事件触发时机回调参数page:add新增页面(page)page:add:before新增页面之前可中止(props, addFn, opts)page:remove删除页面(page)page:remove:before删除页面之前可中止(page, rmFn, opts)page:select切换选中页面(page, previousPage)page:select:before切换选中页面之前(page, opts)page:update页面属性更新(page, changes)page上述所有事件的汇总({ event, page, options })其中page汇总事件最适合 UI 刷新任何与页面本身不含页面内组件/样式内容相关的变更都会触发它。一个最小可用的页面切换栏可以这样实现const pm editor.Pages; let selectedId pm.getSelected()?.id; editor.on(page, () { selectedId pm.getSelected()?.id; renderPageList(); // 重新渲染页面列表 }); function renderPageList() { const container document.getElementById(pages-bar); container.innerHTML ; pm.getAll().forEach(page { const item document.createElement(div); item.textContent page.getName() || page.getId(); item.className page.getId() selectedId ? page-item selected : page-item; item.onclick () pm.select(page.getId()); container.appendChild(item); }); }在这个 UI 中page.getName()用于展示、page.getId()用于选中与去重删除时则用pm.remove(page.getId())完整印证了本文介绍的所有 Page 方法在真实场景中的组合用法。存储与序列化行为Page 在toJSON()中会做一次「瘦身」见 Page.ts剔除skipFromStorage、所有下划线开头的私有键如_undo以及等于默认值的冗余字段。因此getId()/getName()返回的值是稳定的序列化字段而setName()的变更会自然落入项目存储通过 PageManager 的storageKey pages持久化无需额外处理即可让页面名称随项目保存与恢复。小结围绕 docs/api/page.md 文档本篇文章完整覆盖了Page对象六个公开方法的签名、返回类型与底层实现getId()返回页面唯一标识是页面寻址的基础getName()/setName(name)负责页面名称的读写并通过change事件联动page:update/page事件getAllFrames()返回页面全部 Frame支撑多画布框架场景getMainFrame()语义化地获取第一个主框架getMainComponent()直达主框架根组件wrapper是遍历、导出、检索页面内容的入口。结合 Page.ts、pages/index.ts 与 Pages 模块测试 的源码证据你可以确认这些 API 与默认单页行为、UndoManager 集成、存储序列化等机制是一体的。基于它们无论是初始化多页面项目、导出单页代码还是构建自定义 Page Manager 界面都有了明确且可验证的路径。【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考