GrapesJS Property 属性模型完全指南:从配置定义到 CSS 目标同步
【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs
本篇指南系统讲解 GrapesJS 开源 Web 构建框架中样式管理器(Style Manager)的核心数据模型Property(属性)。Property 是样式管理器中每一个可编辑 CSS 属性(如color、text-align、margin)的模型载体,负责值的解析、默认值回退、可见性判断以及与选中目标(组件或 CSS 规则)之间的双向同步。读完本文,你将掌握 Property 的完整配置字段、全部公开 API、内置属性类型体系(number / select / color / composite / stack 等),并能在 docs/api/property.md 的基础上,通过editor.StyleManager自行定义、扩展和联动属性。
Property 在 Style Manager 中的定位
GrapesJS 的 Style Manager 将可编辑样式按"扇区(Sector)— 属性(Property)"两级组织:每个扇区包含一组属性,每个属性对应一个(或一组)CSS 属性。Property 就是这一层级的模型对象,其 API 文档见 docs/api/property.md,而扇区模型的 API 见 docs/api/sector.md。
在编辑器初始化时通过styleManager配置扇区与属性:
const editor = grapesjs.init({ styleManager: { sectors: [ { name: 'My Sector', open: false, properties: [ { property: 'min-height', type: 'select', default: '100px', options: [{ id: '100px', label: '100' }] }, ], }, ], }, });默认配置(见 packages/core/src/style_manager/config/config.ts)内置了General、Flex、Dimension、Typography、Decorations、Extra六个扇区,这些扇区引用的属性名(如display、width、margin、box-shadow)均来自内置属性工厂。
编辑器实例化后,通过editor.StyleManager获取模块,即可用addProperty、getProperty、getProperties、removeProperty、select、addBuiltIn等 API 操作属性(方法清单见 docs/api/style_manager.md)。addProperty返回的正是Property模型实例:
const styleManager = editor.StyleManager; const property = styleManager.addProperty('mySector', { label: 'Minimum height', property: 'min-height', type: 'select', default: '100px', options: [{ id: '100px', label: '100' }, { id: '200px', label: '200' }], }, { at: 0 });Property 基础配置字段(Properties)
创建 Property 时可配置以下基础字段(即 docs/api/property.md 中的Properties小节):
| 字段 | 类型 | 说明 |
|---|---|---|
id | String | 属性 id,例如my-property-id |
property | String | 关联的 CSS 属性名,例如text-align |
default | String | 属性的默认值 |
label | String | 在 UI 中显示的标签,例如Text Align |
onChange | Function? | 值变化回调 |
onChange回调签名及用法:
onChange: ({ property, from, to }) => { console.log(`Changed property`, property.getName(), { from, to }); }在源码 packages/core/src/style_manager/model/Property.ts 中,PropertyProps接口还暴露了更多可用于配置的字段,与上述基础字段共同构成完整的属性定义:
name:显示名称;初始化时会基于property自动推导(capitalize(prop).replace(/-/g, ' ')),因此通常无需手动指定;type:属性类型,决定使用哪个 Property 子类(详见下文类型体系);info:属性说明信息;value:当前值;icon:UI 图标;functionName:CSS 函数名(如url、translateX),配合值渲染成functionName(value)的形式;visible:是否可见,默认true;fixedValues:固定候选值数组,默认['initial', 'inherit'](内置属性工厂另有['initial', 'inherit', 'auto']的变体);full:是否在 UI 中独占整行(full-width),默认false;important:为true时值会被追加!important;toRequire:若为true,属性默认隐藏,仅当目标通过stylable-require显式声明需要它时才显示——官方建议将全部 SVG 相关属性配置为toRequire: true,再在 SVG 组件上按需require;requires:依赖同目标上其他属性值,条件全部满足才显示,例如requires: { display: ['flex', 'block'], position: ['absolute'] };requiresParent:依赖选中目标父元素的计算样式,例如内置的flex-basis依赖父元素display: flex;isVisible:自定义可见性判断函数,例如isVisible: ({ component }) => component?.is('image'),仅图片组件选中时显示;status、className、parentTarget、extend等内部/扩展字段。
defaults()中定义的基础默认值为:name: ''、property: ''、type: ''、defaults: ''、info: ''、value: ''、icon: ''、functionName: ''、status: ''、visible: true、fixedValues: ['initial', 'inherit']、full: false、important: false、toRequire: false。
属性类型体系:type 决定模型与视图
getType()返回属性类型,类型在属性创建时确定,并据此分配对应的 Property 子类,默认类型为base。类型注册表见 packages/core/src/style_manager/model/Properties.ts,内置类型与对应的模型/视图如下:
| type | 模型 | 视图 | 说明 |
|---|---|---|---|
base | Property | PropertyView | 默认类型,通用文本输入 |
number/integer | PropertyNumber | PropertyNumberView | 数值 + 单位输入 |
select | PropertySelect | PropertySelectView | 下拉选择 |
radio | PropertyRadio | PropertyRadioView | 单选按钮组 |
slider | PropertySlider | PropertySliderView | 滑块 |
color | Property | PropertyColorView | 颜色选择器 |
file | Property | PropertyFileView | 文件(如图片 url) |
composite | PropertyComposite | PropertyCompositeView | 组合属性(子属性聚合为一个 CSS 简写) |
stack | PropertyStack | PropertyStackView | 堆叠属性(多层,如 box-shadow) |
内置属性工厂 packages/core/src/style_manager/model/PropertyFactory.ts 预定义了 70+ 个常用 CSS 属性,例如:
- 数值型:
top、margin-*、padding-*、width、height、font-size、line-height等,默认单位集unitsSize = ['px','%','em','rem','vh','vw'],时间单位['s','ms'],角度单位['deg','rad','grad']; - 单选型:
float、position、text-align; - 颜色型:
color(默认black)、background-color、border-color; - 文件型:
background-image(functionName: 'url'); - 滑块型:
opacity(min: 0, max: 1, step: 0.01); - 下拉型:
display、flex-direction、font-family、border-style、overflow、cursor等; - 组合型:
margin、padding、border、border-radius; - 堆叠型:
transition、box-shadow、text-shadow、background、transform。
这些内置定义可通过styleManager.getBuiltIn('width')获取、getBuiltInAll()获取全部、addBuiltIn(prop, definition)新增或扩展。transform还演示了fromStyle/toStyle自定义逻辑:把transform: rotateZ(45deg)拆解为transform-type与transform-value两个子属性。
Property 核心 API 详解
以下方法全部定义在 packages/core/src/style_manager/model/Property.ts,测试用例覆盖见 packages/core/test/specs/style_manager/model/Properties.ts。
标识类方法:getId / getType / getName / getLabel
getId():返回属性 id(this.get('id'))。getType():返回属性类型字符串,默认base。getName():返回 CSS 属性名(内部实际读取的是property字段)。getLabel(opts = {}):返回 UI 标签。可传opts.locale(默认true)决定是否优先使用 i18n 模块的翻译串styleManager.properties.<id>;找不到翻译时回退到name或label字段。
值读取类方法:getValue / hasValue / hasValueParent / getDefaultValue
getValue(opts = {}):返回当前值;当属性没有值且未传noDefault时返回默认值(即!hasValue() && !noDefault ? getDefaultValue() : val)。hasValue(opts = {}):判断属性是否"有值"。判定条件是value非undefined且非空串;若传opts.noParent = true,则忽略来自父目标的值(parentTarget)。测试中hasValue()与hasValue({ noParent: true })的组合被广泛用于验证继承值场景(见 Properties.ts 中 289-316 行等用例)。hasValueParent():指示当前值是否来自父目标(如另一个 CSSRule),等价于hasValue() && !hasValue({ noParent: true })。getDefaultValue():返回默认值,优先读取default字段,回退到defaults字段。
getStyle:生成 CSS 样式对象
getStyle(opts = {})返回该属性对应的 CSS 样式对象。传opts.camelCase可将属性名转为驼峰形式:
// 属性为 color 且值为 red 时 console.log(property.getStyle()); // { color: 'red' };实现上内部调用__getFullValue(),它会根据functionName包装成fn(value)(如url('...')),并在important为真时追加!important。对 composite/stack 类型,getStyle被子类覆写(见 PropertyComposite.ts 的getStyleFromProps),可生成简写或拆分的多属性对象。
upValue:更新值并同步到目标
upValue(value, opts = {})是更新值的主入口,变化会传播到选中的目标(如 CSS 规则):
value:新值字符串;传入null或''时等价于执行清除逻辑;opts.partial:若为true,对目标的更新视为"未完成"(不进入 UndoManager);opts.noTarget:若为true,变化不会传播到选中目标。
调用链为:upValue → __parseValue(parseValue 解析)→ _up(set 触发 change)→ __upTargets → __upTargetsStyle → styleManager.addStyleTargets,最终调用目标的addStyle写入样式,参见 StyleManager.addStyleTargets 与测试中compTypeProp.upValue('1px 2px 3px 4px')后断言rule1.getStyle()的用例。
parseValue还负责解析!important后缀以及 CSS 函数(如translateX(10deg)解析为{ value: 10, unit: 'deg', functionName: 'translateX' }),并支持opts.numeric数值拆分。
可见性与清理类方法:isVisible / clear / canClear
isVisible():返回属性是否可见(读取visible字段)。clear(opts = {}):清除值,同样会传播到选中目标(如清除对应 CSS 属性);传opts.noTarget = true可阻止传播。内部通过__getClearProps()将value置空并携带__clear标记。composite 类型覆写了clear,会递归清除所有子属性。canClear():判断当前值是否直接来自选中目标(因而可以被清除);子属性场景下委托给父属性的__canClearProp。
层级类方法:getParent / isFull
getParent():若当前属性是子属性(composite/stack 的成员),返回其父 Property,否则返回null。isFull():指示属性是否在 UI 中独占整行(读取full字段)。
实战:注册属性、监听变化与自定义类型
注册一个下拉属性并监听变化:
const prop = styleManager.addProperty('typography', { property: 'text-transform', type: 'select', default: 'none', options: [ { id: 'none', label: 'None' }, { id: 'uppercase', label: 'UPPERCASE' }, { id: 'capitalize', label: 'Capitalize' }, ], onChange: ({ property, from, to }) => { console.log(`${property.getName()} changed`, { from, to }); }, }); // 编程式更新值(同步到选中目标) prop.upValue('uppercase');借助 PropertySelect 的动态选项能力(见 PropertySelect.ts):getOptions()获取选项、setOptions([...])整体替换、addOption({ id, label })追加选项、getOptionLabel(id)结合 i18n 返回标签,适用于字体列表等需要异步加载的选项。
Number 属性的单位与范围(见 PropertyNumber.ts):可通过units: ['px','%','em']、min、max、step配置输入约束,使用getUnits()、getUnit()、getMin()、getMax()、getStep()读取,upUnit(unit)更新单位并同步目标。
注册自定义属性类型,用于扩展 UI 控件(见 StyleManager.addType):
styleManager.addType('my-custom-prop', { create({ props, change }) { const el = document.createElement('div'); el.innerHTML = '<input type="range" class="my-input" min="10" max="50"/>'; const inputEl = el.querySelector('.my-input'); inputEl.addEventListener('change', (event) => change({ event })); inputEl.addEventListener('input', (event) => change({ event, partial: true })); return el; }, emit({ props, updateStyle }, { event, partial }) { updateStyle(`${event.target.value}px`, { partial }); }, update({ value, el }) { el.querySelector('.my-input').value = parseInt(value, 10); }, destroy() {}, });之后即可在扇区定义或addProperty中使用type: 'my-custom-prop'。
值同步与父目标机制
Property 的"值"与"目标样式"并非同一存储:属性模型持有value,而实际 CSS 落在目标(组件内联样式或 CSSRule)上。__upTargets通过StyleManager.addStyleTargets({ [name]: value })把值写入所有选中目标;反向地,当选中目标变化时,StyleManager.__upProp会读取目标样式并回填到属性(__up标记避免循环触发)。
当属性自身没有值时,Style Manager 会沿"父规则链"(如标签选择器规则、id 规则等,见getParentRules)寻找继承值,并通过__setParentTarget记录来源,这正是hasValueParent()与hasValue({ noParent: true })存在的原因——UI 上可以据此区分"直接设置的值"与"继承的值",并决定clear()是否可用(canClear())。
总结
Property 是 GrapesJS 样式管理器的最小可编辑单元,本文覆盖了它的全部基础配置字段、9 种内置类型、15 个公开方法及其源码实现路径。实际开发中建议:用addProperty注册业务属性、用onChange做联动、用requires/requiresParent/isVisible控制上下文相关的显隐、用addBuiltIn扩展全局内置属性、用addType实现完全自定义的 UI 控件。相关实现与验证可直接查阅 Property.ts、PropertyFactory.ts、Properties.ts 及测试套件 Properties.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),仅供参考