GrapesJS Layer Manager 完全指南:API、事件与自定义图层树实现
2026/9/11 17:16:15 网站建设 项目流程

GrapesJS Layer Manager 完全指南:API、事件与自定义图层树实现

【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs

Layer Manager(图层管理器)是 GrapesJS 中将组件(Components)以树形结构可视化展示的核心模块,它与画布联动,承担层级查看、排序、显隐切换、锁定、重命名等职责。本文以仓库中 docs/api/layer_manager.md 为主线,结合 packages/core/src/navigator 下的源码实现与测试用例,系统讲解 Layer Manager 的初始化配置、全部公开事件、12 个核心 API 方法及其底层原理,并给出基于layer:custom事件从零实现自定义图层 UI 的完整方案。

Layer Manager 是什么

Layer Manager 负责将编辑器中的 Component(即 Components 体系中的模型)以可展开的树形结构渲染出来,是 GrapesJS 默认 UI 中位于左侧面板的"图层"面板。它具备以下能力:

  • 以树形结构展示组件层级(父子关系、嵌套深度以缩进表示)
  • 拖拽排序、重新组织组件结构
  • 切换组件显示/隐藏(visibility)
  • 展开/折叠(open)与锁定/解锁(locked)
  • 双击重命名组件
  • 点击选中组件并与画布双向联动(悬停高亮、选中滚动定位)

从源码结构看,该模块位于 packages/core/src/navigator,核心类LayerManager继承自Module(见 packages/core/src/navigator/index.ts),视图层由ItemView(packages/core/src/navigator/view/ItemView.ts)与ItemsView(packages/core/src/navigator/view/ItemsView.ts)构成。

初始化与获取模块实例

你可以在编辑器初始化时通过layerManager配置项定制模块的初始状态:

const editor = grapesjs.init({ // ... layerManager: { // ... }, })

编辑器实例化完成后,通过editor.Layers获取模块引用,即可调用其全部 API:

const layers = editor.Layers;

配置项详解

layerManager支持的全部配置项定义在 packages/core/src/navigator/config/config.ts,默认值见同文件config()工厂函数(config.ts#L111-L127):

配置项类型默认值说明
stylePrefixstring''CSS 类名前缀
appendTostring | HTMLElement''指定渲染容器(query 字符串或 DOM 元素);为空则不渲染默认 UI
sortablebooleantrue全局开关:是否允许拖拽排序图层
hidablebooleantrue全局开关:是否显示"隐藏图层"的眼睛图标
hideTextnodebooleantrue是否隐藏文本节点(textnode)与<br>标签
rootstring''作为图层根的组件选择器(query);默认根为 wrapper(根容器组件)。若未指定或未找到对应元素,则回退使用主 wrapper
showWrapperbooleantrue是否在图层树中显示 wrapper 本身
showHoverbooleantrue画布中悬停组件时是否在图层中联动
scrollCanvasboolean | ScrollIntoViewOptions{ behavior: 'smooth', block: 'nearest' }在图层中选中组件时是否滚动画布定位到该组件;block: 'nearest'可避免整页滚动问题
scrollLayersboolean | ScrollIntoViewOptions{ behavior: 'auto', block: 'nearest' }在画布中选中组件时是否滚动图层列表定位
highlightHoverbooleantrue悬停图层项时是否高亮画布对应组件
custombooleanfalsetrue时不渲染默认图层管理器,配合layer:custom事件使用自定义 UI
onInitfunction() => {}⚠️ 实验性:图层项初始化时的回调,可用于监听组件属性变化并触发重渲染
onRenderfunction() => {}⚠️ 实验性:图层项渲染后的回调,可操作图层 DOM
extendobject{}扩展 Layer 视图对象(即ItemView)的方法或属性

一个典型的初始化配置示例:

const editor = grapesjs.init({ // ... layerManager: { // 若未指定 root 或元素未找到,则使用主 wrapper 作为根 root: '#my-custom-root', sortable: false, hidable: false, } });

注意:图层是组件的直接映射,只有组件在编辑器中加载完成(例如从远程端点加载项目数据)后,图层才会可用。

从实现上看,config中的hideTextnode直接影响图层项是否可入树:LayerManager.__isLayerable(index.ts#L353-L359)会排除 textnode、<br>以及layerable属性为假的组件;sortable则在ItemsView构造时决定是否为该容器创建ComponentSorter实例(ItemsView.ts#L41-L61),排序行为通过LayersComponentNode树类与垂直嵌套拖拽策略(DragDirection.Verticalnested: true)实现。

可用事件

Layer Manager 提供 4 个事件,类型定义与回调签名见 packages/core/src/navigator/types.ts:

layer:root

根图层发生变化时触发,回调参数为新的根组件。

editor.on('layer:root', (component) => { ... });

源码依据:LayerManager.__onRootChange(index.ts#L337-L343)在modelroot属性变化时调用em.trigger(events.root, root)

layer:component

组件图层更新时触发(如 open、status、locked、custom-name、components、classes 等属性变化),回调参数为更新后的组件及可选选项。

editor.on('layer:component', (component, opts) => { ... });

源码依据:LayerManager.onLoad中监听了以component:update:*开头的多个属性事件(index.ts#L54-L56、index.ts#L83),统一调用updateLayer(index.ts#L370-L373)触发该事件。

layer:custom

自定义图层事件。当配置custom: true时,用于告知你把自定义 UI 挂载到默认容器中。回调参数为包含containerroot的对象(类型LayerCustomEventData,见 types.ts#L48-L51)。

editor.on('layer:custom', ({ container, root }) => { ... });

源码依据:LayerManager.__trgCustom(index.ts#L361-L368)在根变化等场景被调用,container即默认提供的容器元素。

layer:render

组件图层渲染完成时触发,回调参数为包含component与渲染后图层 DOM 元素el的对象(类型LayerRenderEventData,见 types.ts#L53-L56)。

editor.on('layer:render', ({ component, el }) => { ... });

源码依据:ItemView.__render(ItemView.ts#L449-L455)在每次渲染完成后触发LayerEvents.render,与配置项onRender回调并行执行。

方法 API 详解

模块提供 12 个公开方法。以下逐一说明参数、返回值与底层实现(对应 packages/core/src/navigator/index.ts 中同名方法)。

setRoot(component)

将指定组件设置为图层根,参数可为 Component 实例或选择器字符串;返回设置后的根组件。

const component = editor.getSelected(); layers.setRoot(component);

实现细节(index.ts#L99-L111):传入字符串时会在 wrapper 内执行wrapper.find(component)查找,未找到则回退为 wrapper;同时会通过__getLayerFromComponent支持组件delegate.layer代理映射。

getRoot()

获取当前根图层组件。

const layerRoot = layers.getRoot();

实现细节(index.ts#L119-L121):直接读取模块模型中缓存的root值。

getComponents(component)

获取指定组件的"合法"子图层组件,即排除不可入层的组件(如 textnode 与layerable为假的组件)。

const component = editor.getSelected(); const components = layers.getComponents(component); console.log(components);

返回Array<Component>。实现细节(index.ts#L132-L137):先取全部子组件经__getLayerFromComponent映射,再用__isLayerable过滤。

setOpen(component, value) / isOpen(component)

更新 / 读取组件的图层展开状态。value为布尔值,isOpen返回布尔值。

// 展开指定组件的图层 layers.setOpen(component, true); // 判断是否展开 layers.isOpen(component); // => true | false

实现细节(index.ts#L144-L155):展开状态以open属性存储在组件模型上;ItemView.updateOpening根据该属性切换展开样式类(ItemView.ts#L263-L276)。此外,当画布选中组件变化时,componentChanged(index.ts#L288-L312)会自动展开其所有祖先图层,并支持scrollLayers滚动定位。

setVisible(component, value) / isVisible(component)

更新 / 读取组件的图层可见状态(对应画布中该组件是否display: none)。

// 隐藏组件 layers.setVisible(component, false); // 判断是否可见 layers.isVisible(component); // => true | false

实现细节:setVisible(index.ts#L162-L183)通过操作组件的style.display实现——隐藏时把原display值暂存到__prev-display属性并置为none,恢复时还原原值;同时触发component:toggled事件以同步 Style Manager。isVisible(index.ts#L190-L192)通过判断样式display是否以none开头来确定。该逻辑在测试 packages/core/test/specs/navigator/view/ItemView.ts 中有明确覆盖:display: 'none'时返回false,空值与block均返回true

setLocked(component, value) / isLocked(component)

更新 / 读取组件的图层锁定状态(锁定后组件在画布中不可编辑)。

layers.setLocked(component, true); layers.isLocked(component); // => true | false

实现细节(index.ts#L199-L210):锁定状态以locked属性存储在组件模型上。

setName(component, value) / getName(component)

更新 / 读取组件的图层显示名称。setName将名称写入组件的custom-name属性;getName返回组件当前图层名称。

layers.setName(component, 'My Custom Name'); layers.getName(component); // => 'My Custom Name'

实现细节(index.ts#L217-L228):getName委托给component.getName(),该命名机制会优先使用custom-name,否则回退到标签名等默认规则。双击图层名称进入编辑后,ItemView.handleEditEnd(ItemView.ts#L233-L245)最终也调用model.setName(name)写回。

getLayerData(component)

从组件读取完整的图层数据对象。

const component = editor.getSelected(); const layerData = layers.getLayerData(component); console.log(layerData);

返回对象结构对应LayerData接口(types.ts#L4-L12):

interface LayerData { name: string; // 图层名称 open: boolean; // 是否展开 selected: boolean; // 是否被选中(组件 status 为 'selected') hovered: boolean; // 是否被悬停(组件 status 为 'hovered') visible: boolean; // 是否可见 locked: boolean; // 是否锁定 components: Component[]; // 合法的子图层组件列表 }

实现细节(index.ts#L239-L251):selectedhovered由组件status属性推导,components复用getComponents。该方法与配套的内部方法setLayerData(index.ts#L253-L282,非文档公开 API)共同构成自定义 UI 的数据读写通道。

自定义图层 UI(custom: true)

默认 UI 无法满足复杂需求时,可通过custom: true关闭默认渲染,并结合事件自行实现图层树。基本骨架如下(完整交互式示例见 docs/modules/Layers.md 的 Customization 章节):

const editor = grapesjs.init({ // ... layerManager: { custom: true, // ... }, }); // 用此事件将你的 UI 追加到 GrapesJS 提供的默认容器中。 // 如果你不依赖核心面板,也可以跳过此事件,把 UI 放到其他位置。 editor.on('layer:custom', (props) => { // props.container (HTMLElement) - 默认的容器元素,可将 UI 挂载进来 // props.root (Component) - 当前的根组件 }); // 根图层变化时触发,用于更新 UI 的根节点 editor.on('layer:root', (root) => { // 更新你的 UI 根 }); // 组件更新时触发,用于精准刷新对应图层项 editor.on('layer:component', (component) => { // 更新你的 UI 中对应的图层 });

在自定义实现中,推荐的数据流是:用layers.getLayerData(component)读取每个组件的图层状态渲染节点,用layers.setOpen / setVisible / setLayerData / setName将 UI 交互写回编辑器,并监听layer:component事件按需刷新。可参考docs/modules/Layers.md中附带的 Vue 演示模板(模板注释内含完整拖拽排序、双击重命名、显隐切换、选中/悬停联动的实现,其中Layers.canMove/Components.canMovesource.move(target, { at: index })展示了如何实现拖拽重组)。

总结

Layer Manager 是 GrapesJS 组件体系与 UI 之间的桥梁:editor.Layers上的 12 个公开方法覆盖了图层树的读、写与查询,layer:root / layer:component / layer:custom / layer:render四个事件支撑了默认 UI 与自定义 UI 的双向同步。理解LayerData结构、open / locked / custom-name / status等组件属性与样式display的联动关系,是深入定制该模块的关键。若需更底层的视图行为,可继续阅读 ItemView.ts 的事件绑定与模板渲染逻辑,以及 config.ts 中每个配置项对视图行为的开关作用。

【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询