- 前端
- UI组件
- 设计系统
【免费下载链接】material-components-web
Modular and customizable Material Design UI components for the web
本篇技术指南基于仓库中的 architecture.md 核心文档,深入剖析 Material Components Web(MDC Web)的整体架构:它如何通过 Subsystem/Component 的包划分组织代码,如何用 Sass mixin 复用样式,以及如何以 Foundation/Adapter 分层把业务逻辑与 DOM 彻底解耦,从而支持在 React、Angular 等不同框架间复用同一套组件逻辑。读完本文,你将掌握 MDC Web 的源码组织方式、组件三件套(Component / Foundation / Adapter)的协作关系,以及作为普通消费方应该只与哪一层 API 交互。
总体架构:Subsystem 与 Component 的包划分
MDC Web 将整个代码库拆分为一个个独立的packages/目录(npm 包),每个包要么是一个Subsystem(子系统),要么是一个Component(组件)。
- Subsystem(子系统):被多个组件复用的基础能力,通常描述样式(例如颜色,见 mdc-theme)或动效(例如动画与缓动曲线,见 mdc-animation 及其 README 中提供的
$standard-curve-timing-function等 Sass 变量)。 - Component(组件):面向用户的具体 UI 部件(如 Button、Checkbox、Dialog、Snackbar 等),趋向于依赖多个子系统包,但很少依赖其他组件包。例如按钮包的 package.json 依赖了
@material/density、@material/dom、@material/elevation、@material/feature-targeting、@material/focus-ring、@material/ripple、@material/rtl、@material/shape、@material/theme、@material/tokens、@material/touch-target、@material/typography等十余个子系统,但并没有依赖 checkbox、dialog 之类的组件包。
这种依赖方向保证了架构的核心约束,也是原文档强调的第一条设计原则:
每个组件都可以独立于其他任何组件单独使用(Each component is usable separate from any other component)。
从代码上看,每个组件包的结构高度一致:以 mdc-checkbox 为例,包含component.ts(Vanilla 组件)、foundation.ts(Foundation)、adapter.ts(Adapter 接口)、constants.ts(cssClasses/strings/numbers 常量)、index.ts(导出入口)以及一系列_*.scss样式文件,这为"按需引入单个组件"提供了天然的物理边界。
Sass:以 mixin 复用的样式体系
MDC Web 的全部 CSS 都由Sass生成。原文档指出:
- Sassmixin让开发者可以把一组可复用的 CSS 声明组织起来,应用到多个组件上;
- 子系统提供 Sass mixin,组件在自己的 Sass 文件中导入这些 mixin;
- 每个包将自己的 Sass 文件编译为一个单独的 CSS 文件。
这一模式在源码中可以直接验证。按钮的基础样式文件 _button-base.scss 顶部集中@use了 elevation、feature-targeting、ripple-theme、rtl、dom、touch-target、focus-ring、typography 等多个子系统模块,并定义static-styles($query)这类 mixin 来组装.mdc-button的结构样式;而 _index.scss 只做了一件事——@forward './button-theme',把主题 mixin 转发给使用方。也就是说,组件的样式文件是"子系统 mixin 的组装车间",每个组件最终产出一个独立的 CSS 文件,配合@use "@material/button"即可引入。
借助@material/feature-targeting子系统,mixin 还支持按structure、color等目标(query)按需产出样式,实现"只编译用到的部分",这是 MDC Web 样式体系可定制、可裁剪的关键机制。
HTML:不提供模板,只提供结构文档
原文档明确声明:
MDC Web 不提供任何 HTML 模板(MDC Web does NOT provide any HTML templates)。我们只是在文档中给出所需的 HTML 结构。
这意味着 MDC Web 与"开箱即用、带完整 DOM 骨架"的 UI 库不同:使用方需要按照各组件 README 中给出的结构自己编写 HTML(例如 checkbox 组件要求的.mdc-checkbox包裹结构与原生<input type="checkbox">),再引入对应的 CSS 与 JS 完成升级(upgrade)。该策略让组件样式与业务 DOM 完全解耦,也为接入各种框架(通过模板/JSX 渲染 DOM)留下了空间。
JavaScript 分层设计:Foundation / Adapter / Vanilla Component
为什么拆分 Foundation 与 Adapter
MDC Web 把每个动态组件的 JavaScript 拆成两半:**Foundation(基座)**与Adapter(适配器)。这样做的直接收益是:
我们可以在多个 Web 平台(例如 React 和 Angular)上复用 Foundation 代码,只需要重新实现 Adapter 即可。目前我们只实现了 Adapter 的 vanilla JavaScript 版本。
也就是说,组件中"最接近 Material Design 规范"的那部分逻辑只写一次(Foundation),平台相关的那部分(DOM 读写、事件监听等)通过接口(Adapter)隔离,换平台只换 Adapter。这一"业务内核 + 平台适配"的思路,是 MDC Web 能被各类框架封装(wrapper)复用的根本原因。
TypeScript 与发布产物
MDC Web 组件使用 TypeScript 编写,以提升开发效率并减少错误。npm 发布产物包括:
- UMD JavaScript bundles(
package.json的main指向 dist 下的 UMD 模块,兼容性最好); - ES Modules(仅含 ES5 语法,配合
module字段供 Webpack/Rollup 等工具做 tree-shaking); .d.ts类型声明文件(供 TypeScript 用户消费,编译器通过types字段自动发现)。
具体的导入方式(ES Modules、CommonJS、AMD、Global/CDN 以及document.querySelectorAll批量实例化)详见 导入 JS 指南。
Foundation:不含 DOM 引用的业务逻辑
Foundation 承载"最能代表 Material Design 的业务逻辑",但不直接引用任何 DOM 元素;凡是需要操作 DOM 的逻辑,都委托给 Adapter 方法完成。从基类 foundation.ts 可以看到这套设计:
MDCFoundation<AdapterType>构造函数接收 adapter(默认空对象);- 提供静态 getter
cssClasses、strings、numbers用于导出组件所需的 CSS 类、语义字符串和数字常量; - 提供
init()与destroy()钩子,分别执行初始化和反初始化例程(如注册/注销事件)。
以 checkbox 为例,MDCCheckboxFoundation 覆盖了这些静态 getter,并在构造时用{...MDCCheckboxFoundation.defaultAdapter, ...adapter}将调用方传入的(可能是部分的)adapter 与默认空实现合并,保证任何时刻 Foundation 调用的 adapter 方法都有兜底;它的setDisabled(disabled)就直接调this.adapter.setNativeControlDisabled(disabled),而不是触碰 DOM。
Adapter:框架无关的接口契约
Adapter 是"Foundation 实现 Material Design 业务逻辑所需的全部方法的接口",可以有多个实现,从而实现与不同框架的互操作。每个组件的 adapter 接口都定义在独立的adapter.ts中。例如 MDCCheckboxAdapter 声明了addClass、forceLayout、hasNativeControl、isAttachedToDOM、isChecked、isIndeterminate、removeClass、removeNativeControlAttr、setNativeControlAttr、setNativeControlDisabled等方法——这些正是复选框的选中态、不确定态、禁用态、动画同步等业务逻辑所需的全部"能力点"。
Vanilla Component:默认实现与 getDefaultFoundation
Vanilla Component 以根元素(rootElement)实例化,它通过覆盖基类MDCComponent的getDefaultFoundation方法,创建"Foundation 实例 + Vanilla Adapter"的组合;Vanilla Adapter 实现 Adapter 接口并直接引用根元素;同时 Vanilla Component 还会为开发者需要访问的 Foundation 方法暴露代理方法(proxy)。
从基类 component.ts 可以看清组件实例化的完整流程:
- 构造函数接收
root与可选的foundation,先调用initialize(...args)(子类可在此做构造期准备); - 若未显式传入 foundation,则调用
getDefaultFoundation()生成——基类默认实现会直接抛错,强制子类覆盖(component.ts#L73-L80); - 调用
this.foundation.init()初始化 Foundation; - 调用
initialSyncWithDOM()让组件与宿主 DOM 同步(例如在根元素上注册change、animationend事件监听,见 checkbox component.ts#L99-L110); destroy()时依次执行清理,并调用this.foundation.destroy()。
基类还提供了listen/unlisten事件封装与emit自定义事件发射方法,组件在需要时也会在自身之上组合其他子系统组件(例如 checkbox 组合了MDCRipple实现水波纹反馈)。
面向消费方的结论
原文档对使用方式给出了非常明确的分工建议:
只想消费 MDC Web(而不是提供封装库)的开发者,只需与 Component 交互,不需要直接访问 Foundation 或 Adapter 的 API。
这也正是各组件 README 中new MDCCheckbox(document.querySelector('.mdc-checkbox'))这类用法的由来——日常使用只面对 Component 这个门面。
组件间的依赖规则与整体组装
汇总包 material-components-web/index.ts 展示了"组件互不依赖、由聚合层统一编排"的最终形态:它把 banner、checkbox、chips、data-table、dialog、drawer、menu、select、slider、snackbar、tab、textfield、tooltip、top-app-bar 等组件统一注册到autoInit(通过data-mdc-auto-init属性自动初始化),并把它们全部导出。也就是说,单组件可独立使用,整套引入则由聚合包完成。
结合前文可见,MDC Web 架构的核心是三条清晰的分层规则:
- 包层面:Subsystem 提供基础能力,Component 依赖 Subsystem,组件之间彼此独立;
- 样式层面:Sass mixin 复用 + 每包单一 CSS 输出,支持按需裁剪;
- 逻辑层面:Foundation(纯业务)— Adapter(接口契约)— Vanilla Component(默认实现)三段式,让业务内核跨框架复用成为可能。
延伸阅读
- 导入 JS 组件指南:ES Modules / CommonJS / AMD / CDN 四种导入方式与批量实例化写法
- 架构文档原文
- 基础包源码:
component.ts与foundation.ts基类实现 - 复选框示例:
adapter.ts/foundation.ts/component.ts三件套的完整参照 - 按钮包依赖清单:组件依赖子系统包的实际证据
- 前端
- UI组件
- 设计系统
【免费下载链接】material-components-web
Modular and customizable Material Design UI components for the web
相关推荐
Spotube架构设计:分层架构与模块划分
Spotube架构设计:分层架构与模块划分 Spotube作为一款开源跨平台的Spotify客户端,采用现代化的分层架构设计,确保代码的可维护性和扩展性。本文将
音视频跨平台从示例到实战:gba crate游戏开发完整案例解析
从示例到实战:gba crate游戏开发完整案例解析 gba crate是一款专为GBA游戏开发打造的Rust库,它提供了丰富的硬件抽象和便捷的开发工具,让开发
Foundation Sites Sass架构与样式系统
Foundation Sites Sass架构与样式系统 Foundation Sites的Sass架构建立在强大的变量系统、丰富的混合函数和实用的工具函数之上
前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考