GrapesJS Component API 完全指南:掌握模板节点模型的核心能力

GrapesJS Component API 完全指南:掌握模板节点模型的核心能力 GrapesJS Component API 完全指南掌握模板节点模型的核心能力【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs导读Component组件是 GrapesJS 模板结构的核心节点对象理解它的 API 是进行二次开发、自定义组件类型与操控画布内容的基础。本文以docs/api/component.md为骨架结合packages/core/src/dom_components/model/Component.ts的源码实现系统讲解 Component 的全部属性、生命周期钩子与常用方法帮助你在编辑器实例中完成对节点树的高效增删改查、样式与属性管理、Traits 配置及 HTML 导出等实战操作。Component 是什么模板树中的单一节点在 GrapesJS 中整个模板Template本质上是一棵由 Component 节点组成的树页面中的每个元素——无论是文本、图片、视频还是自定义组件——都被抽象为一个 Component 对象。文档开篇即点明其核心定位The Component object represents a single node of our template structure, so when you update its properties the changes are immediately reflected on the canvas and in the code to export.这意味着更新 Component 的属性Properties后改动会立即同步反映到画布与导出代码中——GrapesJS 在导出代码时就是递归遍历整棵节点树来生成 HTML。因此掌握 Component 的属性读写方式就掌握了操控编辑器的遥控器。最基本的读写方式是set/getcomponent.set({ tagName: span, attributes: { title: Hello }, removable: false, }); component.get(tagName); // - span在源码 Component.ts 中Component类继承自StyleableModel一个可样式化、可序列化的 Backbone 风格模型并通过defaultsgetter 定义了全部内置属性的默认值。type、tagName、attributes、traits等属性都存储于模型属性中这也是set/get能直接操作它们的原因。属性详解从type到delegate以下属性是 Component 模型的内置配置项可以在组件定义、set()调用或自定义组件类型中直接使用。本节完整覆盖文档中的属性清单并补充源码默认值与内部实现要点。标识与渲染属性类型默认值说明typeString组件类型如text、image、video等决定使用哪个已注册的组件类型模型tagNameStringdiv组件的 HTML 标签如spanattributesObject{}组件的属性键值对如{ title: Hello }nameString组件名称用于 Layers 面板与画布上的 badge 徽标显示iconString组件图标字符串插入在名称之前Layers 与 badge 中可为 HTML 字符串如i classfa fa-square-o/ivoidBooleanfalseHTML 导出器使用void 元素没有闭合标签如br/、hr/void属性的实际效果可在 toHTML 实现 中看到当没有内部内容且void为真时输出自闭合形式的标签skipEndTag逻辑。交互与可操作性属性类型默认值说明removableBooleantrue为false时组件不可从画布移除draggableBoolean \| String \| Functiontrue是否可被拖入其他组件内部。可传 CSS 查询串限定可拖入的目标如.some-class[titleHello], [data-gjs-typecolumn]仅可拖入含some-class类与Hellotitle 的元素及column类型组件传函数时目标target与目的地destination组件作为参数传入返回布尔值决定是否允许拖拽droppableBoolean \| String \| Functiontrue是否允许其他组件放入自身内部查询串/函数用法与draggable一致badgableBooleantrue设为false则不在组件上方显示 badge含名称的徽标highlightableBooleantrue为true时可用虚线边框高亮copyableBooleantrue是否允许克隆组件resizableBoolean \| Objectfalse是否可调整组件大小也可传对象作为 Resizer 的选项editableBooleanfalse是否允许编辑组件内容用于 Text 组件layerableBooleantrue为false时组件在 Layers 面板中隐藏selectableBooleantrue点击时是否允许被选中hoverableBooleantrue为true时悬停元素显示高亮轮廓lockedBooleanundefined禁用画布中组件及其子级的选中可将子级locked设为false单独解锁draggable、copyable、removable三个属性不仅控制交互行为还直接影响选中组件时工具栏Toolbar中自动生成的按钮。在 initToolbar 方法 中可以看到当toolbar属性为假值时编辑器会根据draggable添加tlb-move、根据copyable添加tlb-clone、根据removable添加tlb-delete存在父级时还会添加core:component-exit选中父组件按钮。样式与内容属性类型默认值说明stylableBoolean \| ArrayStringtrue是否可对组件设置样式传数组如[color, width]时表示只有这些 CSS 属性在 Style Manager 中可见stylable-requireArrayString[]显示被标记为toRequire的样式属性数组unstylableArrayString[]在 Style Manager 中隐藏的样式属性数组styleObject组件默认样式如{ width: 100px, height: 100px, background-color: red }stylesString组件相关样式如.my-component-class { color: red }contentString组件内容不转义在子组件渲染前追加脚本与数据属性类型默认值说明scriptString \| Function组件的 JavaScript详见 Components-js 模块script-exportString \| Function仅用于导出函数如获取 HTML 时的 JavaScript定义了它会在导出时覆盖scripttraitsArrayObject \| String[id, title]组件的 Traits 特性配置详见 Traits 模块propagateArrayString[]指定会被所有新追加的子组件继承的属性数组。例如{ removable: false, draggable: false, propagate: [removable, draggable] }后追加的新子组件会获得完全相同的属性值包括propagate自身componentsCollectionComponentnull子组件集合工具栏与委托属性类型默认值说明toolbarArrayObjectnull选中组件时工具栏中显示的自定义项目数组如toolbar: [ { attributes: {class: fa fa-arrows}, command: tlb-move }, ... ]。为假值时编辑器自动添加core:component-exit有父组件时、tlb-movedraggable时、tlb-clonecopyable时、tlb-deleteremovable时delegateObjectnull将命令委托给其他组件。可用命令remove|move|copy|select如{ remove: (cmp) cmp.closestType(other-type) }生命周期钩子init / updated / removedComponent 模型提供三个可覆写的生命周期钩子用于在组件模型的关键节点执行自定义逻辑init()—— 模型创建时调用一次。自定义组件类型时常用它来初始化内部状态、绑定事件或设置默认子结构。updated(property, value, previous)—— 模型被更新时调用如更新了某个属性。参数property属性名、value属性新值、previous属性旧值仅在属性更新后触发时传入。removed()—— 模型被移除后调用一次可用于清理外部引用或释放资源。这些钩子通常结合editor.Components.addType()在自定义组件类型的model定义中覆写使用。类型判断与树形检索Component 提供了一套完整的类型判断与节点检索 API覆盖渲染前/渲染后两种场景。类型判断component.is(image); // - false若当前组件不是 image 类型源码中 is 方法 直接比较this.get(type)与传入类型。isInstanceOf(type)检查组件是否属于某个组件类型的实例支持继承链判断。源码 isInstanceOf 通过instanceof与typeExtends集合处理多级继承// 通过扩展现有类型新增一个组件类型 editor.Components.addType(text-ext, { extend: text }); const newTextExt editor.getSelected().append({ type: text-ext })[0]; newTextExt.isInstanceOf(text-ext); // true newTextExt.isInstanceOf(text); // true继承自 textisChildOf(component)检查组件是否为某个组件或某组件类型的子级。传入字符串时按组件类型匹配const newTextComponent editor.getSelected().append({ type: text, components: My text bhere/b, })[0]; const innerComponent newTextComponent.find(b)[0]; innerComponent.isChildOf(newTextComponent); // true innerComponent.isChildOf(text); // true其实现源码会沿parent()链向上遍历判断。向下检索find / findType / findFirstTypefind(query)—— 通过 CSS 查询串查找内部组件返回组件数组component.find(div .class); // - [Component, Component, ...]注意文档明确警告find仅对已渲染的组件生效实现依赖this.view.$el.find(query)见 源码。findType(query, opts)—— 按组件类型查找所有内部组件。与find相比它不依赖渲染状态在组件渲染前即可使用是推荐方式const allImages component.findType(image); console.log(allImages[0]); // 第一个匹配组件 // 也支持函数匹配器并通过 opts.max 限制匹配数量 const someComponents component.findType((cmp) cmp.getType() something, { max: 2 });源码实现findType使用getComponentMatcher将字符串/函数统一为匹配函数并深度优先递归遍历components()集合命中max上限后提前退出。findFirstType(query)—— 查找第一个匹配的组件无匹配时返回undefinedconst image component.findFirstType(image); if (image) { console.log(image); } const firstImage component.findFirstType((cmp) cmp.is(image));其实现即findType(query, { max: 1 }).at(0)见 源码。向上检索closest / closestType / containsclosest(query)—— 按查询串查找最近的父级组件同样仅适用于已渲染的组件component.closest(div.some-class); // - ComponentclosestType(query)—— 按类型查找最近父级组件不依赖渲染状态。实现源码从parent()起沿链向上直到匹配器返回真值const Section component.closestType(section); console.log(Section); const namedSection component.closestType((cmp) cmp.getName() Section);contains(component)—— 返回传入组件是否为当前组件的后代const isDescendant component.contains(otherComponent); // Boolean属性操作attributes、style 与 classesattributes 三件套setAttributes(attrs, opts)—— 更新组件属性整体替换 attributes 对象component.setAttributes({ id: test, data-key: value });addAttributes(attrs, opts)—— 在现有属性上追加新键值内部会先读取当前属性再合并见 源码component.addAttributes({ data-key: value });removeAttributes(attrs, opts)—— 移除单个或多个属性component.removeAttributes(some-attr); component.removeAttributes([some-attr1, some-attr2]);三者均返回this便于链式调用。底层 setAttributes 通过this.set(attributes, { ...attrs }, opts)完成模型更新opts支持SetAttrOptions默认{ skipWatcherUpdates: false, fromDataSource: false }用于数据源绑定场景。getAttributes(opts)—— 返回组件全部属性。opts支持{ noClass: boolean, noStyle: boolean, skipResolve: boolean }分别用于排除 class、style 以及跳过数据解析。style 读写getStyle(opts)—— 获取组件样式对象。实现源码中有一个关键行为当编辑器配置了avoidInline避免内联样式时会改为从 CSS 规则em.Css.getIdRule中读取样式而非内联 style。setStyle(prop, opts)—— 设置组件样式component.setStyle({ color: red });class 管理四个方法覆盖了 class 的增、改、删、查均接受字符串或数组支持空格分隔的字符串model.addClass(class1); model.addClass(class1 class2); model.addClass([class1, class2]); // - [SelectorObject, ...]返回新增的 Selector 数组 model.setClass(class1 class2); // 重置当前 class 集合 model.removeClass([class1, class2]); // 移除后返回被移除的 Selector 数组 component.getClasses(); // - [class1, class2]字符串数组setClass会重置当前集合后添加源码 setClass适用于整体替换。子组件树管理append 与 componentsappend(components, opts)—— 追加子组件接受 Component、HTML 字符串或二者数组返回追加后的组件数组someComponent.get(components).length; // - 0 const videoComponent someComponent.append(video/videodiv/div)[0]; // 向 someComponent 添加了 2 个组件video 和 div someComponent.get(components).length; // - 2 // 也可以直接传入组件对象 otherComponent.append(otherComponent2); otherComponent.append([otherComponent3, otherComponent4]); // 指定插入位置如开头 someComponent.append(otherComponent, { at: 0 });components()—— 双用途方法无参时返回当前子组件集合传参时重置集合并追加新内容// 设置新集合 component.components(span/spandiv/div); // 获取当前集合 const collection component.components(); console.log(collection.length); // - 2其源码实现components先coll.reset(undefined, opts)清空再append。定位与清空// 返回指定索引的子组件不存在则返回 null/undefined component.getChildAt(0); // 第一个子组件 component.getChildAt(1); // 第二个子组件 // 返回最后一个子组件 const lastChild component.getLastChild(); // 清空所有内部组件 component.empty(); // 返回 this父级访问component.parent(); // - Component 或 null component.parents(); // - [Component, Component, ...]从直接父级到根的所有祖先parents 实现 递归拼接父级链parent(opts)支持传{ prev: true }读取移动前的原父级prevColl。replaceWith / remove / movereplaceWith(el, opts)—— 用其他组件或 HTML 字符串替换当前组件const result component.replaceWith(divSome new content/div); // result - [Component]替换产生的新组件数组实现源码先记录当前位置at移除自身后在原位置插入新组件。remove(opts)—— 从画布/树中移除组件返回this。move(component, opts)—— 将当前组件移动到目标组件内部作为其子级// 把当前选中组件移动到 wrapper 顶部 const dest editor.getWrapper(); editor.getSelected().move(dest, { at: 0 });实现源码会智能处理同父级内的索引位移sameParent且目标索引大于当前索引时at减 1并通过临时移除temporary: 1 追加完成移动保证撤销栈记录正确。Traits 特性管理Traits 是组件在属性面板Trait Manager中暴露的可配置项默认值为[id, title]。Component 提供了一套完整的 Traits 操作 APIgetTraits()—— 返回当前 Traits 数组const traits component.getTraits(); // [Trait, Trait, Trait, ...]setTraits(traits)—— 用新的定义数组整体替换 Traits 集合const traits component.setTraits([{ type: checkbox, name: disabled }, ...]); // [Trait, ...]getTrait(id)—— 按 id/name 获取单个 Trait未找到返回nullconst traitTitle component.getTrait(title); traitTitle traitTitle.set(label, New label);updateTrait(id, props)—— 更新某个 Trait 的属性如动态切换类型与选项component.updateTrait(title, { type: select, options: [Option 1, Option 2], });getTraitIndex(id)—— 返回 Trait 在集合中的索引位置便于运行时替换const traitTitle component.getTraitIndex(title); console.log(traitTitle); // 1removeTrait(id)/addTrait(trait, opts)—— 移除与新增 Traitcomponent.removeTrait(title); component.removeTrait([title, id]); component.addTrait(title, { at: 1 }); // at 为插入位置索引 component.addTrait({ type: checkbox, name: disabled, }); component.addTrait([title, {...}, ...]);HTML 导出toHTML / getInnerHTMLtoHTML(opts)返回组件完整 HTML 字符串是导出功能的核心方法。opts支持以下选项选项类型说明tagString自定义 tagName覆盖组件当前标签attributesObject \| Function传对象时整体替换当前属性传函数时接收(component, attributes)动态生成属性并返回withPropsBoolean将组件属性以data-gjs-*属性形式写入 HTML得到可重新导入re-importable的 HTMLaltQuoteAttrBoolean属性值含时用单引号包裹attrvalue 而非转义attrvalue quot;基础用法与动态属性示例// 简单 HTML 返回 component.set({ tagName: span }); component.setAttributes({ title: Hello }); component.toHTML(); // - span titleHello/span // 自定义属性 component.toHTML({ attributes: { data-test: Hello } }); // - span>// 获取/更新名称 component.getName(); // 返回名称字符串 component.getName({ noCustom: true }); // 忽略自定义名称 component.setName(New name); // 更新名称 // 图标 component.getIcon(); // 返回图标字符串 // ID component.getId(); // 返回组件 id component.setId(new-id); // 设置新 id返回 this // 仅已渲染组件可用 component.getEl(frame); // 获取 DOM 元素可指定 Frame component.getView(frame); // 获取对应 ComponentView可指定 FramegetEl/getView在源码中均依赖已渲染的视图实例getEl多 Frame 场景下可传入Frame指定获取哪个画布中的元素。遍历与元信息onAll(clb)—— 对自身及所有内部组件执行回调含递归返回thiscomponent.onAll(component { // do something with component });实现源码先执行自身回调再对每个子组件递归调用onAll。forEachChild(clb)—— 仅对全部子组件执行回调不含自身component.forEachChild(child { console.log(child); });其他元信息方法component.props(); // 返回全部属性对象内部 attributes component.index(); // 返回组件在父集合中的索引无父集合时为 0 component.getChangedProps(res); // 仅返回有变化的属性PartialComponentDefinitionprops()直接返回this.attributesindex()通过collection.indexOf(this)计算见 源码。拖拽模式absolute / translatesetDragMode(value)/getDragMode()用于切换组件的拖拽模式可选值为absolute|translate|component.setDragMode(absolute); // 切换为绝对定位拖拽 component.getDragMode(); // - absolute底层通过dmode属性存储见 源码。absolute模式在拖拽时直接修改 top/left 坐标translate模式使用 CSS transform 实现更平滑的移动适合实现画布内的自由布局编辑。Symbols 覆盖setSymbolOverride / getSymbolOverrideGrapesJS 的 Symbols符号功能参见 Symbols 指南允许复用组件并保持一致性Main Symbol 的修改会自动同步到所有 Instance Symbol。而setSymbolOverride提供了局部脱离同步的能力component.setSymbolOverride([children, classes]);传true时该组件后续任何属性变更都不会再传播到相关 Symbols传属性名数组时仅这些属性的变更会跳过传播其余仍保持同步传字符串等价于单元素数组。getSymbolOverride()返回当前的覆盖值Boolean或ArrayString。内部通过 Symbol 专用的隐藏属性源码中的keySymbolOvrd见 setSymbolOverride存储。典型场景是某个 Instance 需要在保持 Symbol 关联的同时允许用户独立修改个别属性如颜色而不会污染所有实例。综合实战组合运用 Component API下面是一个综合示例演示如何将上述 API 组合用于常见的选中组件后动态改造场景// 1. 获取当前选中组件并确认类型 const cmp editor.getSelected(); if (!cmp || !cmp.isInstanceOf(text)) return; // 2. 调整标签、属性与样式 cmp.set({ tagName: span }); cmp.addAttributes({ data-key: value, title: Hello }); cmp.setStyle({ color: red }); // 3. 追加子组件并定位到第一个 const added cmp.append(strongBold text/strong, { at: 0 }); console.log(added[0].getName()); // 4. 在树中检索与验证 const strong cmp.find(strong)[0]; console.log(strong.isChildOf(cmp)); // true console.log(cmp.contains(strong)); // true console.log(cmp.findType(text).length); // 渲染前也可用 // 5. 导出验证 console.log(cmp.toHTML({ withProps: true }));结语Component 是 GrapesJS 数据模型与画布交互之间的枢纽所有属性变更即时映射到画布与导出代码。本文从属性清单、生命周期钩子、树形检索、属性/样式/类操作、子组件管理、Traits、HTML 导出、拖拽模式到 Symbols 覆盖完整覆盖了 Component API 文档 的全部内容并结合 Component.ts 源码 揭示了各方法的底层实现逻辑。掌握这套 API 后你可以自信地编写自定义组件类型、构建程序化模板编辑逻辑或实现高级的批量操作能力。若需了解组件集合Components Collection与类型注册机制可继续阅读 Components 模块 与 组件集合 API。【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考