ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

@css-blocks/core 内核全解析:从 Block 文件到最终 CSS 的完整编译管线

@css-blocks/core 内核全解析:从 Block 文件到最终 CSS 的完整编译管线 前端构建工具【免费下载链接】css-blocksHigh performance, maintainable stylesheets.项目地址https://gitcode.com/gh_mirrors/cs/css-blocks点击查看免费下载css-blocks/core是 CSS Blocks 生态中承上启下的核心引擎包它驱动了从读取一个 Block 文件到输出最终 CSS之间的所有环节Block 语法BlockSyntax解析、BlockTree内存数据模型构建、BlockFactory工厂与预处理调度、BlockCompiler编译与冲突解析、模板分析Analyzer/Analysis/ElementAnalysis以及自定义Importer扩展。读完本文你将掌握 core 包的完整模块划分、全部配置项及其源码级默认值、BlockTree 的四层节点模型、BEM 输出模式的命名规则以及如何编写自定义 Importer 接入block导入解析。一、css-blocks/core 在项目中的定位在 CSS Blocks 的 monorepo 中css-blocks/core是一个几乎自成 mono-repo的基础包。按官方 README 的表述它负责驱动读取 Block 文件与输出最终 CSS之间发生的一切。具体来说这个包承载了全部 CSS Blocks 专属语法常量与解析函数BlockSyntax构建BlockTree所需的全部节点类型Block、BlockClass、Attribute、AttrValue所有Analyzer的基类以及模板级/元素级分析数据模型BlockFactory与BlockParser的实现含预处理集成BlockCompiler编译器含冲突解析选择器的生成构建配置工具与自定义Importer接口。因为承载的功能面广该包内部按包中包的方式组织源码目录即模块边界BlockTree、BlockCompiler、BlockParser、BlockSyntax、Analyzer、TemplateRewriter、configuration、importing、util。下文按此结构逐一展开。二、配置项Options全解析core 包的构建配置通过Configuration对象传入。原文档给出了如下配置表结合 src/configuration/types.ts 与 src/configuration/resolver.ts 中的实际默认值实现完整的配置项如下配置项默认值说明rootDirprocess.cwd()所有源码的相对路径均以此为基准的根目录。outputModeBEMBlock 文件的输出模式取值为OutputMode枚举BEM或BEM_UNIQUE。preprocessors{}按Syntax语法类型声明预处理函数异步版本。preprocessorsSync{}按语法类型声明的同步预处理函数源码中新增README 未列全。importerNodeJsImporter用于解析block指令传入标识符的自定义导入器。importerData{}提供给 importer 的额外数据命名空间化的自定义数据区域。maxConcurrentCompiles4同时进行的 Block 解析/编译数量上限。disablePreprocessChainingfalse若为css声明了预处理函数则所有 Block包括其他语法预处理过的都会再跑一遍 css 预处理设为true可关闭该链式行为。guidAutogenCharacters5生成 Block GUID 时使用的有效字符数源码新增README 未列全。配置解析的源码实现src/configuration/resolver.ts 中的resolveConfiguration(options, defaults?)是配置归一化入口它把OptionsPartialReadonlyConfiguration与内置DEFAULTS合并产出一个只读的ResolvedConfiguration。合并优先级为内置默认值 → process.cwd() 填充 rootDir → 传入的 defaults → 用户 optionsimporterData与preprocessors这类对象型键会做浅拷贝合并其余简单键直接覆盖。值得注意的两个隐藏配置项preprocessorsSync同步预处理函数集合。当构建工具无法容忍异步如某些原生绑定的预处理场景时使用见 src/configuration/types.ts。guidAutogenCharacters控制由文件唯一标识符通常是绝对路径哈希生成 GUID 的有效字符位数。默认 5 个字符正常情况下无需修改仅在极为罕见的 GUID 冲突时增大该值。该配置不影响从其他依赖导入的预编译 Block——此类 Block 的 ID 由定义文件中预先写定的 GUID 决定见 src/configuration/types.ts。OutputMode 枚举src/configuration/OutputMode.ts 定义了两个输出模式export enum OutputMode { BEM BEM, BEM_UNIQUE BEM_UNIQUE, }两者的差异体现在BlockClass.cssClass()的命名生成逻辑中见 src/BlockTree/BlockClass.tsBEM根类:scope输出为blockName普通类输出为blockName__className若生成的名称与保留类名如 Block 别名生成的名字冲突则退化为追加 GUID 的形式blockName_guid/blockName_guid__classNameBEM_UNIQUE始终追加 GUID根类输出blockName_guid普通类输出blockName_guid__className保证全局唯一。三、BlockTreeBlock 文件的内存数据模型BlockTree是 core 包的核心抽象它把单个 Block 文件中蕴含的全部复杂性捕获为内存数据模型并对外暴露查询与操作的 API。3.1 强类型 N 叉树的四层结构BlockTree是强类型子节点必须为单一类型的N 叉树每一层可有任意数量的子节点由四种节点类型构成且必须按如下层级排列Block BlockClass Attribute AttrValue所有节点类型的公共基类实现在 src/BlockTree/Inheritable.ts。Inheritable抽象类通过 TypeScript 泛型自引用Self/Root/Parent/Child/Token构造了一套类型安全的通用树节点骨架并提供与树层级、继承/解析相关的公共 APIbase沿父节点继承链查找并缓存当前节点继承自哪个 StyleresolveInheritance()返回按继承顺序排列的祖先节点数组resolveChild()/resolveChildren()/resolveChildrenMap()在自身及继承链上解析子节点getParsedSelectors(rule)根节点维护整棵树的解析选择器缓存WeakMappostcss.Rule, ParsedSelector[]避免重复解析。3.2 容器节点Container Nodes容器节点负责容纳同类节点的逻辑分组并可按需实现针对所容纳节点类型的便捷方法。Block永远是BlockTree的根节点可容纳任意数量的BlockClass子节点。:scope选择器被视为一种特殊的BlockClass同样作为 Block 的子节点存储。Block 节点还保存所有block引用数据、block-name、实现的 Blocksimplements、继承的 Block 以及 Block 文件中的其他元数据。Block 有一个特殊的rootClass属性直接指向代表:scope选择器的子BlockClass见 src/BlockTree/Block.ts。Attribute代表唯一的namespace|name键值对可容纳任意数量的AttrValue子节点该 Attribute 在 Block 文件中发现的所有可能取值。Attribute 的父节点永远是BlockClass。未指定值的属性选择器被视为一种特殊的AttrValue同样存储为 Attribute 的子节点。Attribute 暴露面向子AttrValue的查询 API。3.3 样式节点Style Nodes样式节点代表完全解析后的选择器其 CSS 规则集会应用到匹配元素上。在 css-blocks 中BlockClass与AttrValue属于样式节点。样式节点继承自抽象类 src/BlockTree/Style.ts在Inheritable基础上增补了RulesetContainer对象用于跟踪所有指向该 Style 及其伪元素的规则集中的属性关注点property concerns与属性解析结果同时提供查询自身及继承生成的类名的方法。BlockClass代表 Block 文件中发现的类选择器可包含一到多个Attribute容器节点并提供查询自身/继承的Attribute及其AttrValue节点的方法。BlockClass还持有样式组合composition信息composedStyles()返回该样式组合引用的其他样式及触发条件Composition { style, conditions, path }见 src/BlockTree/BlockClass.ts。AttrValue代表完全限定的属性选择器namespace、name、value 均已定义是叶子节点不能有子节点。3.4 各节点汇总所有BlockTree对象及其 API 使 core 能够以易遍历、可查询的格式在内存中构建任意 Block 文件、其依赖及其全部数据的完整表示。Block上还提供lookup()/externalLookup()按 BlockPath 语法如.foo、[active]、other.bar、other.root解析样式引用以及getReferencedBlock/getExportedBlock的正反向查询见 src/BlockTree/Block.ts。四、BlockParser 与 BlockFactory解析与预处理BlockParser 包包含所有把 Block 文件转换为BlockTree的构造包括预处理集成。4.1 BlockFactory单例化与并发控制BlockFactory是 BlockParser 交付的主类采用工厂模式Factory Pattern绝大多数消费者只需与BlockFactory交互来创建Block无需关心底层 parser。BlockFactory的核心职责与实现见 src/BlockParser/BlockFactory.ts保证每个唯一 Block 文件只解析一次getBlock(identifier)先查this.blocks缓存再查this.promises进行中的 Promise若同一文件被再次请求直接返回之前那次解析的同一 Promise最终解析为同一个共享Block对象BlockFactory.ts。预处理完成之后才开始编译BlockFactory只在所有用户提供的预处理步骤结束后才开始 Block 编译。预处理通过PromiseQueue调度其并发上限正是配置中的maxConcurrentCompiles默认 4见 BlockFactory.ts。错误重试若上次解析失败getBlock会捕获错误并在下次请求时重新发起解析同时保证错误对象携带正确的导入堆栈BlockFactory.ts。GUID 唯一性校验Block 的 GUID 由文件标识符哈希生成BlockFactory负责注册并校验 GUID 全局唯一冲突时报错并提示检查依赖中的 GUID 冲突或增大 guidAutogenCharactersBlockFactory.ts。退出安全prepareForExit()在有进行中的预处理任务时调用preprocessQueue.drain()等待全部异步工作完成后才安全退出进程BlockFactory.ts。4.2 BlockParser特征中间件流水线在引擎盖下BlockFactory使用BlockParser把给定的postcss.Root转换为新的Block对象。BlockParser借鉴了 Builder Pattern 的思路将新建的Block依次送入一系列按语言特性划分的中间件。每个中间件位于 src/BlockParser/features只负责读取并增强 Block 的某一个语言特性关注点。一条关键约束解析阶段绝不能以任何方式转换传入的postcss.Root。PostCSS 树在所有BlockParser特性中间件中应视为只读仅用于构建最终的BlockTree。此外block-intermediates中间数据结构在BlockParser之外唯一被使用的位置是BlockTree的一处官方计划将其重构为BlockParser包完全私有的构造。4.3 预处理Preprocessors与 Syntaxsrc/BlockParser/preprocessing.ts 定义了预处理契约export enum Syntax { sass sass, scss scss, css css, less less, stylus styl, other other, }Preprocessors是语法 → 预处理函数的映射键必须是Syntax的成员。预处理函数签名type Preprocessor (fullPath: string, content: string, configuration: ResolvedConfiguration, sourceMap?: RawSourceMap | string) PromiseProcessedFile;ProcessedFile返回content字符串或postcss.Result、可选sourceMap与dependencies声明文件依赖用于构建/缓存的正确失效。同文件还提供了annotateCssContentWithSourceMap()便捷函数当预处理输出需要内联 source map 时把 source map 以sourceMappingURL注释形式追加到 CSS 内容尾部preprocessing.ts。五、BlockSyntax语法常量与解析函数BlockSyntax包提供 CSS Blocks 专属的语法常量与少量简单解析函数供 Block 文件使用。项目约定所有 Block 文件中使用的 CSS Blocks 专属语法都必须定义在这里其他包不得重新定义这些常量或解析函数以保证语法定义唯一。核心常量包括ROOT_CLASS:scope的根类标识、ATTR_PRESENT无值属性选择器的特殊值、CLASS_NAME_IDENT类名标识符正则以及BlockPath解析器等见 src/BlockSyntax。六、BlockCompiler编译与冲突解析BlockCompiler包交付一个类BlockCompiler。它的职责是接收一个Block对象、一个postcss.Root以及可选的Analyzer以指导最终样式表输出返回转换后的postcss.Root其中所有类与状态都被替换为全局唯一的输出名称并且所有解析resolution与继承选择器都被发射到样式表中。注当前 master 分支并不接收完整的Analyzer只接收单个Analysis官方认为未来应当支持完整Analyzer。大部分编译工作本质上是查找并替换把 Block 文件中的所有类与状态替换为生成的、全局唯一的类名命名格式由配置中的OutputMode决定默认 BEM命名规则见上文第二节。冲突解析选择器的生成一旦需要发射冲突解析选择器编译就复杂起来。当存在显式解析或一个 Block 继承自另一个 Block 并重定义了继承的 CSS 属性时必须发射冲突解析选择器使浏览器呈现预期行为。这涉及合并两个可能相当复杂的选择器使新选择器仅在两个被覆盖的选择器同时生效时匹配。原文档给出的完整示例输入/* other.css */ :scope { block-name: other; } :scope[active] .bar { color: blue; } /* main.css */ block other from ./other.css; :scope { block-name: main; } :scope:hover .foo { color: red; color: resolve(other.bar); }输出/* Compiled other.css */ .other--active .other__bar { color: blue; } /* Compiled main.css */ .main:hover .main__foo { color: red; } /* Emitted Resolution Selector */ .other--active.main:hover .main__foo.other__bar { color: blue; }可见main.css中.foo在:hover时声明color: red同时通过resolve(other.bar)声明了对other块中.bar蓝色属性的解析编译后两个选择器被合并为.other--active.main:hover .main__foo.other__bar只有两个覆盖场景同时成立时蓝色才会生效从而精确还原作者的意图。Block上的rewriteSelector()/rewriteSelectorNodes()即负责把解析后的选择器节点逐个替换为输出类名见 src/BlockTree/Block.ts。七、Analyzer / Analysis / ElementAnalysis模板分析三件套Analyzer包交付模板集成Template Integration所需的类、数据模型与类型。模板集成的职责是给定若干模板入口爬取模板依赖树并分析发现的每个模板的每个元素。驱动每个模板分析器集成的有三个核心类。7.1 Analyzer所有模板集成的基类Analyzer是所有模板集成必须继承的基类代表从提供的入口列表可达的全部模板的项目级分析。继承者需要实现抽象方法analyze(...entry-points)构建集成正是通过调用它来启动分析src/Analyzer/Analyzer.ts。Analyzer提供newAnalysis()工厂方法为每个发现的模板创建一个新的Analysis对象随后Analyzer爬取模板内容并把该模板的全部 Block 使用数据记录在对应Analysis上。Analyzer还提供一系列便捷方法eachAnalysis、analyses()、analysisCount()、staticCount()/dynamicCount()、isDynamic(style)等用于访问和遍历一次分析产生的Analysis对象reset()方法可清空上一轮分析的所有缓存与数据使Analysis对象可在开发重建等场景中复用Analyzer.ts。此外Analyzer内部维护staticStyles与dynamicStyles两个MultiMap分别记录静态与动态使用的样式及所属 Analysis供优化阶段消费。7.2 Analysis单个模板的 Block 使用数据Analysis对象代表单个模板的 Block 使用数据由Analyzer在分析过程中为爬取模板依赖树时发现的每个模板创建。模板集成负责组装每个Analysis使其准确反映模板中的 Block 使用情况包括把所有被引用的 Block 添加到Analysis为模板中的每个元素创建新的ElementAnalysis并注册所有被使用的 Block 样式。Analysis同时充当ElementAnalysis的工厂发现新元素时模板集成调用当前模板Analysis的startElement()创建新的ElementAnalysis注册完所有 Block 样式后调用endElement()密封该ElementAnalysis。7.3 ElementAnalysis单个元素的 Block 使用数据ElementAnalysis代表单个元素的 Block 使用数据通过Analysis.startElement()工厂取得。最近一次返回的ElementAnalysis在Analysis.endElement()被调用前保持未密封状态。未密封的ElementAnalysis可用于存储保存到其中的 Block 样式使用数据即BlockClass与AttrValue。模板中使用的任何 Block 样式可能属于以下三种类型之一Static静态、Dynamic动态、Mutually Exclusive互斥。以如下 Block 文件为例.my-class { /* ... */ } .other-class { /* ... */ } [active] { /* ... */ } [colorred] { /* ... */ } [colorblue] { /* ... */ }静态样式保证永不改变div block:classmy-class block:activetrue/div动态样式可能根据应用状态决定是否应用div block:class{{style-if value my-class}} block:active{{isActive}}/div互斥样式保证不会同时出现在同一元素上{{!-- my-class 与 other-class 互斥 --}} {{!-- [colorred] 与 [colorblue] 互斥 --}} div block:class{{style-if value my-class other-class}} block:color{{color}}/div每个模板集成的 Block 消费语法各不相同集成需自行实现模板解析与 Block 对象发现以喂给ElementAnalysis的 API。7.4 密封后的自动验证器一旦ElementAnalysis被密封会对其自动运行一系列校验确保没有违反模板规则。这些模板验证器是独立的插件可单独启用或禁用默认全部启用。验证器位于 src/Analyzer/validationsattribute-group-validator验证任何给定的 State 属性在元素上只应用一次attribute-parent-validator确保 State 属性总是与其所属类同时应用class-pairs-validator若同一 Block 的两个类同时应用于同一元素抛错原文档拼写为 class-paris-validator仓库实际目录为 class-pairs-validator-testproperty-conflict-validator若两个样式可能同时应用于同一元素且存在未解析的冲突属性关注点抛错root-class-validator禁止:scope类与BlockClass应用于同一元素。这些校验器在 src/Analyzer/validations 下均有对应的实现与测试见 test/validations 中的 attribute-group-validator-test.ts、attribute-parent-validator-test.ts、class-pairs-validator-test.ts、property-conflict-validator-test.ts、root-class-validator-test.ts。八、TemplateRewriter重写数据模型由于每个模板集成必须借助其模板系统提供的插件/AST 转换机制Rewriter 比 Analyzer 更自由没有统一的基类可供扩展。TemplateRewriter包交付的是数据模型模板集成可在重写阶段利用它们查询如何重写遇到的元素。构建集成负责把这些重写数据传递给实际的 rewriter 集成。注官方 README 也坦承当前 Analyzer 到 Rewriter 的数据交接方式未标准化、与构建集成耦合过紧例如 Vue 集成可能因 Broccoli 目前高度 Glimmer 化而需要独立的 Broccoli 构建集成并认为这种模式难以扩展。相关数据模型实现可参阅 src/TemplateRewriterRewriteMapping、ClassRewrite、StyleMapping、AggregateRewriteData。九、configuration构建配置工具configuration包包含 CSS Blocks 构建配置工具配置哈希的 TypeScript 类型、用于把用户提供的配置哈希与默认值归一化的配置读取器resolveConfiguration。具体类型与默认值实现已在第二节详述可参见src/configuration/types.tsConfiguration、Options、ResolvedConfiguration类型定义src/configuration/resolver.ts默认值与合并逻辑src/configuration/OutputMode.ts输出模式枚举src/configuration/index.ts包导出入口。十、importing自定义 Importer 扩展点CSS Blocks 需要知道当block给出一个文件标识符FileIdentifier时从哪里获取 Block 文件内容。大多数情况下该路径就是磁盘上的文件此时内置默认 importer 可直接工作而当需要自定义block的解析行为时消费者可实现 CSS Blocks 的Importer接口来注入自定义逻辑。自定义 importer 通过配置对象的importer选项传给 CSS Blocks用于抽象应用/平台特定的路径解析逻辑。10.1 Importer 接口任何 CSS BlocksImporter必须实现 src/importing/Importer.ts 中定义的接口原 README 指向的types.ts在当前仓库中已并入Importer.ts。每个 importer 必须提供若干内省方法为给定FileIdentifier返回标准元数据identifier为该FileIdentifier返回全局唯一标识符defaultName若未设置block-name返回要使用的默认 Block 名filesystemPath若该FileIdentifier由文件系统支撑返回绝对文件路径debugIdentifier返回供人阅读、标识该文件的字符串用于调试与错误报告syntax返回该文件内容所用语法类型取值来自Syntax枚举。但任何 importer 的主方法是import()它返回一个 Promiseresolve 为一个元数据对象——不仅包含上述全部信息还包含文件的字符串内容contents。正是这些内容BlockFactory将用来创建BlockTree。需要传递额外数据给自定义 importer 时配置中的importerData选项被专门保留为 importer 额外数据的命名空间区域所有 importer 方法都会把完整的 CSS Blocks 配置对象作为最后一个参数传入。10.2 NodeJsImporter内置默认导入器CSS Blocks 内置一个预定义 importerNodeJsImportersrc/importing/NodeJsImporter.ts。它是未提供其他 importer 时的默认实现支持block解析相对与绝对文件引用路径按以下算法解析若是绝对路径直接解析到指定位置若是相对路径先尝试相对于包含该引用的文件解析若相对路径在磁盘上匹配不到文件则测试相对导入路径的首段是否匹配任一配置的 aliasAlias { alias, path }用该 alias 的路径把导入路径变为绝对路径最后任何相对路径再对配置中的rootDir解析。从源码看NodeJsImporter的identifier()实际解析顺序还要更细NodeJsImporter.ts绝对路径直通相对/绝对文件存在则返回alias 匹配则拼接 alias 路径随后尝试require.resolve以文件方式解析 node_modules 中的 Block 文件扩展名须在css或配置的 preprocessors 键中再尝试按包名解析——读取包内package.json的css-blocks.main字段默认blocks/index.block.css定位入口 Block 文件以上都失败则返回最初计算的期望绝对路径。构造时 alias 会按路径长度从长到短排序最具体优先并校验 alias 路径必须是绝对路径NodeJsImporter.ts。此外NodeJsImporter的import()会检测编译后的 Block CSS含 CSS Blocks 头注释若是则走importCompiledBlockSync路径解析其中的定义 URLdata:text/css;base64内嵌数据或外部定义文件路径重建ImportedCompiledCssFile含blockId、cssContents、definitionContents等见 src/importing/Importer.ts从而支持跨依赖导入预编译 Block。十一、util仓库内部工具src/util 提供仓库内部使用的工具PromiseQueue将一系列任务排队并行执行若某任务失败会等待所有运行中的任务完成或失败后再 reject。BlockFactory正是用它调度预处理任务并实现maxConcurrentCompiles并发上限。unionInto类似Object.assign但针对Set做并集合并。Block.resolveImplementedBlocks()等继承/实现解析逻辑依赖它见 src/BlockTree/Block.ts。十二、总结core 包的完整工作流综合全包源码css-blocks/core的典型工作流可概括为配置resolveConfiguration归一化用户配置outputMode、rootDir、preprocessors、importer、maxConcurrentCompiles 等产出只读ResolvedConfiguration导入NodeJsImporter或自定义 importer按block标识符解析并读取文件内容返回ImportedFile或ImportedCompiledCssFile预处理BlockFactory通过PromiseQueue并发上限 4调度语法对应的预处理函数全部完成后才进入编译解析BlockParser把只读的postcss.Root经一系列特性中间件转换为BlockTreeBlock→BlockClass→Attribute→AttrValueBlockFactory保证同一文件只解析一次并校验 GUID 唯一性模板分析Analyzer基类驱动模板集成创建Analysis与ElementAnalysis记录静态/动态/互斥样式密封后运行五个验证器编译BlockCompiler按OutputMode把所有类与状态替换为全局唯一名称并在继承/显式解析场景发射冲突解析选择器最终产出可直接交付的 CSS。通过这份模块地图无论是接入新模板系统继承Analyzer、实现重写数据消费、定制block解析实现Importer还是调整编译输出OutputMode/guidAutogenCharacters你都能在 core 包中精准定位对应的扩展点。赞分享前端构建工具【免费下载链接】css-blocksHigh performance, maintainable stylesheets.项目地址https://gitcode.com/gh_mirrors/cs/css-blocks点击查看免费下载相关推荐CSS Blocks核心概念解析从Block到Scope的完整教程CSS Blocks核心概念解析从Block到Scope的完整教程 CSS Blocks是一个革命性的高性能、可维护样式表系统专为设计系统和应用组件打造。如前端构建工具探索CSS Blocks核心包css-blocks/core如何打造高性能可维护样式表探索CSS Blocks核心包css blocks/core如何打造高性能可维护样式表 CSS Blocks是一个专注于构建高性能、可维护样式表的开源项目前端构建工具CSS Blocks面向组件的高性能 CSS 创作系统——从 Block 语法到构建期静态分析完整指南CSS Blocks面向组件的高性能 CSS 创作系统——从 Block 语法到构建期静态分析完整指南 CSS Blocks 是 LinkedIn 开源的组件前端构建工具上一篇电子书转有声书:ebook2audiobook 把本地书籍合成为带章节的有声书下一篇CANN 多流推理调试指南kernel_details.csv 字段查法与物理并行验证创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表