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):
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
stylePrefix | string | '' | CSS 类名前缀 |
appendTo | string | HTMLElement | '' | 指定渲染容器(query 字符串或 DOM 元素);为空则不渲染默认 UI |
sortable | boolean | true | 全局开关:是否允许拖拽排序图层 |
hidable | boolean | true | 全局开关:是否显示"隐藏图层"的眼睛图标 |
hideTextnode | boolean | true | 是否隐藏文本节点(textnode)与<br>标签 |
root | string | '' | 作为图层根的组件选择器(query);默认根为 wrapper(根容器组件)。若未指定或未找到对应元素,则回退使用主 wrapper |
showWrapper | boolean | true | 是否在图层树中显示 wrapper 本身 |
showHover | boolean | true | 画布中悬停组件时是否在图层中联动 |
scrollCanvas | boolean | ScrollIntoViewOptions | { behavior: 'smooth', block: 'nearest' } | 在图层中选中组件时是否滚动画布定位到该组件;block: 'nearest'可避免整页滚动问题 |
scrollLayers | boolean | ScrollIntoViewOptions | { behavior: 'auto', block: 'nearest' } | 在画布中选中组件时是否滚动图层列表定位 |
highlightHover | boolean | true | 悬停图层项时是否高亮画布对应组件 |
custom | boolean | false | 为true时不渲染默认图层管理器,配合layer:custom事件使用自定义 UI |
onInit | function | () => {} | ⚠️ 实验性:图层项初始化时的回调,可用于监听组件属性变化并触发重渲染 |
onRender | function | () => {} | ⚠️ 实验性:图层项渲染后的回调,可操作图层 DOM |
extend | object | {} | 扩展 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.Vertical、nested: true)实现。
可用事件
Layer Manager 提供 4 个事件,类型定义与回调签名见 packages/core/src/navigator/types.ts:
layer:root
根图层发生变化时触发,回调参数为新的根组件。
editor.on('layer:root', (component) => { ... });源码依据:LayerManager.__onRootChange(index.ts#L337-L343)在model的root属性变化时调用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 挂载到默认容器中。回调参数为包含container与root的对象(类型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):selected与hovered由组件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.canMove与source.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),仅供参考