尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

GrapesJS CssRule 完整解析:CSS 规则模型、at-rule 封装与 toCSS 序列化实战

GrapesJS CssRule 完整解析:CSS 规则模型、at-rule 封装与 toCSS 序列化实战 GrapesJS CssRule 完整解析CSS 规则模型、at-rule 封装与 toCSS 序列化实战【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjsCssRule 是 GrapesJS 中管理样式规则的核心模型位于 CssRule.ts负责将选择器、状态、媒体查询、!important与嵌套样式组合成可序列化的 CSS 字符串。本文围绕 docs/api/css_rule.md 中的 API 文档结合源码实现与测试用例深入讲解 CssRule 的每个属性、每个方法的底层行为与真实应用场景。读完本文你将能够熟练使用editor.Css.setRule/getRule创建、查询和修改规则理解getAtRule、selectorsToString、getDeclaration、toCSS的完整输出逻辑并掌握media、font-face、嵌套规则等高级用法的细节。CssRule 是什么一条 CSS 规则的模型化表示在 GrapesJS 中画布里的每一条 CSS 规则class 规则、id 规则、状态规则、媒体查询规则等都会实例化为一个 CssRule 模型并统一存放在editor.CssCssComposer 模块的规则集合 CssRules.ts 中。CssRule 继承自StyleableModel见 StyleableModel.ts因此天然具备getStyle、setStyle、addStyle、removeStyle、styleToString等样式操作能力同时叠加了选择器管理与 CSS 序列化逻辑。从 CssRule.ts 的defaults()可以看到模型的完整默认状态defaults() { return { selectors: [], // 选择器集合 selectorsAdd: ,// 额外字符串选择器 style: {}, // 样式定义对象 mediaText: , // at-rule 条件如 (max-width: 1000px) state: , // 状态如 hover stylable: true, // 是否可从编辑器样式化 atRuleType: , // at-rule 类型如 media singleAtRule: false, // 仅用于 page/font-face 等纯声明块 important: false,// 是否追加 !important group: , // 规则分组 shallow: false, // 为 true 时不入 JSON、不参与 CSS 导出 _undo: true, // 参与撤销/重做 }; }完整属性说明属性类型默认值说明selectorsArraySelector[]选择器数组元素为 Selector 模型class/idstyleObject{}样式定义对象如{ color: red }selectorsAddString额外追加的字符串选择器如div#myidatRuleTypeStringat-rule 类型如media、font-face、supports、container、pagemediaTextStringat-rule 条件值如(max-width: 1000px)singleAtRuleBooleanfalse仅用于page/font-face这类只含声明块的 at-rulestateString规则状态如hover、focusedimportantBoolean \| ArrayStringfalse为true时所有属性追加!important传数组可指定属性stylableBooleantrue是否允许从编辑器内样式化groupString规则分组标识shallowBooleanfalse为true时规则不入 JSON 序列化也不出现在 CSS 导出中其中selectorsAdd、atRuleType、state、important、stylable在 CssRule.ts 的CssRuleProperties接口中均有对应声明group与shallow则是接口层面额外补充的属性对导出与存储行为有直接影响shallow规则在 CssRules.ts 的toJSON中被过滤shallow不会被写入项目 JSON 或导出 CSS。构造与选择器归一化CssRule 的构造函数签名来自 docs/api/css_rule.mdnew CssRule(props: CssRuleProperties, opt: any {})实际实现中CssRule.ts构造时做了两件事调用ensureSelectors将传入的 selectors 归一化为Selectors集合——如果传字符串会包装成数组并经由em.Selectors去重合并确保同一选择器只存在一份调用setStyle(this.get(style), { skipWatcherUpdates: true })触发一次样式解析然后监听change事件将变更通过em.changesUp上报给编辑器从而驱动画布重渲染与撤销栈记录。getAtRule还原完整的 at-rule 语句getAtRule()返回规则对应的 at-rule 前缀语句例如media (min-width: 500px)、keyframes、container somename (min-width: 300px)。文档示例const cssRule editor.Css.setRule(.class1, { color: red }, { atRuleType: media, atRuleParams: (min-width: 500px) }); cssRule.getAtRule(); // media (min-width: 500px)其底层实现由静态方法getAtRuleFromProps完成CssRule.tsstatic getAtRuleFromProps(cssRuleProps: PartialCssRuleProperties) { const type cssRuleProps.atRuleType; const condition cssRuleProps.mediaText; const typeStr type ? ${type} : condition ? media : ; return typeStr (condition typeStr ? ${condition} : ); }推导规则很清晰atRuleType存在时前缀为type若同时存在mediaText中间以空格连接形成media (max-width: 1000px)、container somename (min-width: 300px)这类语句atRuleType为空但mediaText存在时自动补全为media两者都为空时返回空字符串。注意atRuleParams是setRule选项层的参数名模型内部实际存储为mediaText见 CssComposer index.ts 的RuleOptions定义这也是文档示例中二者混用的原因。测试用例 CssModels.ts 验证了带命名的容器查询container somename (min-width: 300px){.test1{height:100px;}}selectorsToString把选择器拼成字符串selectorsToString(opts)将规则的选择器、状态与selectorsAdd组合为字符串。文档示例const cssRule editor.Css.setRule(.class1:hover, { color: red }); cssRule.selectorsToString(); // .class1:hover cssRule.selectorsToString({ skipState: true }); // .class1实现见 CssRule.ts先通过getSelectors().getFullString(0, { escape })得到主选择器串其中使用CSS.escape做转义不可用时退回原字符串若state非空且未传skipState追加:state若selectorsAdd非空作为追加片段与主选择器以,连接。因此一条selectors: [.class1], state: hover, selectorsAdd: div#myid的规则会得到.class1:hover, div#myid。该输出是后续序列化和规则去重cache key的基础CssComposer index.ts 的_makeCacheKey/_makeCacheKeyFromProps都基于getAtRule()与selectorsToString()的结果构建。getDeclaration生成不带 at-rule 的声明块getDeclaration(opts)返回选择器与花括号包裹的样式块不包含 at-rule 前缀。文档示例const cssRule editor.Css.setRule(.class1, { color: red }, { atRuleType: media, atRuleParams: (min-width: 500px) }); cssRule.getDeclaration() // .class1{color:red;}实现CssRule.ts的关键分支调用selectorsToString(opts)得到选择器串调用styleToString({ important, ...opts })将样式对象序列化为color:red;形式important取自模型属性singleAtRule为true时直接返回纯样式块不带选择器这正是font-face、page的行为嵌套规则opts.nested存在nestedStyleKey时输出nestedStyleKey{style}例如.bar{color:red;}只有选择器或 singleAtRule且存在样式时才输出allowEmpty可强制输出空块。样式序列化时还包含一条重要规则值为空字符串的样式属性会被清除见 StyleableModel.ts这是 GrapesJS 中置空即删除样式语义的来源。toCSS输出完整 CSS 字符串toCSS(opts)是getAtRule()与getDeclaration()的组合产物文档示例const cssRule editor.Css.setRule(.class1, { color: red }, { atRuleType: media, atRuleParams: (min-width: 500px) }); cssRule.toCSS() // media (min-width: 500px){.class1{color:red;}}实现CssRule.ts先取声明块再在最外层包上 at-ruletoCSS(opts: ToCssOptions {}) { let result ; const atRule this.getAtRule(); const block this.getDeclaration(opts); if (block || opts.allowEmpty) result block; if (atRule result) result ${atRule}{${result}}; return result; }测试用例CssModels.ts覆盖了丰富的边界情形可作为行为契约参考无样式或选择器时返回空字符串普通规则输出.test1{color:red;}媒体规则输出media (max-width: 768px){.test1{color:red;}}命名容器规则输出container somename (min-width: 300px){.test1{height:100px;}}泛化 at-rulesupports、font-face与singleAtRule模式各有对应输出嵌套样式规则obj.toCSS()默认输出.test1{color:green;}传{ withNested: true }后才输出内嵌的.bar{color:red;}——嵌套规则默认不参与序列化。嵌套规则Nested CSS当样式键以、、.,#,:,[,,,~,*开头且值为对象时会被识别为嵌套规则见 CssRule.ts 的isNestedStyleKey。嵌套值会被转换为独立的子 CssRule通过parentRule与nestedStyleKey关联父规则const isNestedStyleKey (key: string) /^(||[.#:[~*])/.test(key);测试展示了其行为CssModels.tsgetStyle(.bar, { withNested: true })返回的是一个 CssRule 实例且obj.getStyle()不带 withNested不会包含嵌套键。嵌套规则会被自动注册到em.Css.getAll()删除父规则时同步解除关联__detachNestedRule。toJSON时嵌套规则会被展开为普通样式对象CssRule.ts保证项目数据仍是纯 JSON。getDevice定位规则所属设备getDevice()返回规则关联的 Device 模型规则为mediaat-rule 时通过mediaText匹配设备宽度const device rule.getDevice(); console.log(device?.getName());实现CssRule.ts逻辑若规则不是media类型或无mediaText返回默认设备宽度媒体查询为空的那一个或null否则遍历em.Devices.getDevices()用getMediaLengthCssGenerator.ts从媒体查询中提取数值与设备getWidthMedia()比对命中则返回对应设备否则null。getState获取规则状态对象getState()返回规则关联的 State 模型如hover对应的 State 实例const state rule.getState(); console.log(state?.getLabel());实现CssRule.ts遍历em.Selectors.getStates()按 State 的name如hover与规则的state值匹配。State 模型定义在 State.ts包含name与label两个属性getName()返回状态名getLabel()在未显式设置 label 时回退到 name。因此通过getState()可以拿到编辑器中展示用的本地化标签。getComponent回溯组件专属规则getComponent()仅对组件专属规则ID 选择器规则有效返回对应的 Component 模型const cmp rule.getComponent(); console.log(cmp?.toHTML());实现CssRule.ts要求规则恰好只有一个选择器且该选择器是 ID 类型随后用 ID 名在em.Components中查找组件查不到则返回null。这一方法让样式层与组件层可以双向追溯拿到一条#myid规则就能拿到它作用的组件并输出其 HTML。规则去重与 compare集合层面的行为CssComposer 模块index.ts通过add/get/addCollection/setRule/getRule/getRules/remove/clear等 API 管理规则集合其核心是同一条规则只存在一份的去重语义_makeCacheKeyFromProps以atRule__selectors为键建立缓存get命中缓存直接返回_findRule使用compare(selectors, state, width, ruleProps)CssRule.ts在集合中线性匹配。compare对选择器做排序后逐一比对无视顺序、并逐一比对state、mediaText、selectorsAdd、atRuleType当传入宽度而未指定 atRuleType 时自动补全为mediaif (wd !atRule) atRule media测试CssModels.ts验证了相同选择器集合返回true状态或媒体条件不同返回falsecompare([s2, s1], hover, 1000)在排序后也能匹配成功。setRule是文档示例中最常用的入口它先用 CSS 解析器parserCss.checkNode解析选择器字符串拆分出state与selectorsAdd再走add流程最后根据addStyles决定addStyle合并还是setStyle覆盖——这是已存在规则时合并样式的实现基础。从编辑器初始化到画布渲染CSS 规则最终通过视图层渲染进画布。CssRuleViewCssRuleView.ts将每个规则渲染为一个style标签render()调用model.toCSS({ important })生成 CSS 文本并在写入 DOM 前后依次触发css:mount:before与css:mount事件外部可通过editor.on(css:mount, ({ rule, ruleView, css }) ...)监听事件定义见 css_composer/types.ts。这意味着你通过 API 修改任意规则的 style画布样式都会即时同步——这正是 GrapesJS 可视化编辑体验的底层机制。初始化时可在编辑器配置中注入初始样式const editor grapesjs.init({ cssComposer: { // 默认样式规则CSS 字符串数组或规则对象 rules: [], } });cssComposer配置项定义于 config.tsstylePrefix默认css-界面样式前缀与rules默认[]初始规则。在onLoad阶段index.ts模块会把em.config.style或配置的rules一次性add进集合实现打开编辑器即存在基础样式的效果。实战组合一条响应式、带状态的规则从创建到导出把本文的知识点串起来一个典型的实战流程如下const css editor.Css; // 1. 创建普通类规则 const rule css.setRule(.btn, { color: red, background-color: #fff }); console.log(rule.toCSS()); // .btn{color:red;background-color:#fff;} // 2. 追加 hover 状态addStyles 合并已有样式 const ruleHover css.setRule(.btn:hover, { color: blue }); console.log(ruleHover.selectorsToString()); // .btn:hover console.log(ruleHover.selectorsToString({ skipState: true })); // .btn // 3. 创建媒体查询规则宽度自动归并为 media const ruleMedia css.setRule(.btn, { font-size: 14px }, { atRuleType: media, atRuleParams: (max-width: 768px), }); console.log(ruleMedia.getAtRule()); // media (max-width: 768px) console.log(ruleMedia.getDeclaration()); // .btn{font-size:14px;} console.log(ruleMedia.toCSS()); // media (max-width: 768px){.btn{font-size:14px;}} // 4. 查询与回溯 const found css.getRule(.btn:hover); console.log(found.getState()?.getName()); // hover console.log(found.getDevice()?.getName()); // 命中宽度则返回对应设备否则默认设备/null // 5. 序列化导出Code Manager 的核心调用 const code editor.getCss(); console.log(code);注意第 3 步中atRuleParams是setRule选项的参数名模型内部映射为mediaTextgetDeclaration()与toCSS()的差异恰好体现了声明块 vs 完整规则两个层级。editor.getCss()内部由 CssGenerator.ts 的build方法完成它遍历cssc.getAll()跳过嵌套规则将普通规则与 at-rule 规则分组sortMediaObject对min-width类媒体查询按数值升序排序并支持onlyMatched仅输出与组件匹配的规则、keepUnusedStyles、allowEmpty、json等选项——这就是编辑器内样式与导出 CSS 一致的保证。小结CssRule 是 GrapesJS 样式体系的基石模型getAtRule负责 at-rule 前缀selectorsToString负责选择器状态追加选择器的拼接getDeclaration负责样式块toCSS将三者组装成最终 CSSgetDevice/getState/getComponent则将规则与设备、状态、组件三个维度打通compare与缓存机制保证了规则的唯一性。无论你是通过editor.Css.setRule做程序化样式注入还是通过 Code Manager 导出模板 CSS理解 CssRule 的序列化链路CssRule.ts CssGenerator.ts都是掌握 GrapesJS 样式能力的关键一步。【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表