分层HTML组件系统:从零构建可维护的前端架构
在业务迭代中很多前端页面越写越乱同样的按钮散落在不同文件里改一个圆角要全局搜索替换底部的弹窗、提示条、表单控件互相依赖牵一发而动全身。这个问题的根源往往不是开发者不细心而是页面缺少一套清晰的分层结构。本文围绕“分层 HTML 组件系统”这一主题从基础概念、分层原则、完整代码示例到工程化建议做一个系统化拆解帮助读者从零搭建一套可维护、可扩展的前端组件体系。1. 什么是分层 HTML 组件系统1.1 组件的“层”到底是什么先说结论分层 HTML 组件系统就是把页面里的 UI 元素按照“职责粗细”和“依赖方向”划分成不同层次然后让每一层只负责自己范围内的事情。常见分层方式有以下三种。原子层基础 UI 元素按钮、输入框、标签、标题、图标这一类最小粒度的 UI 单元。它们不包含业务含义只关注自身的样式和交互。组合层复合组件把多个原子组件拼装成更完整的模块。例如搜索框 输入框 搜索按钮分页组件 一组按钮 当前页码状态卡片 标题区 内容区 操作区。业务层页面级组件由组合层组件拼接出完整的业务场景。例如商品卡片、用户信息面板、订单操作栏。这一层通常离业务数据最近也最容易因为需求变化而频繁修改。“分层”的核心思想是下层组件不依赖上层组件上层组件可以自由组合下层组件。好比搭积木底层是标准化的积木块上层决定怎么拼。1.2 为什么要做分层很多初学者会产生疑问我直接写一个包含按钮、输入框、弹窗的页面文件不也能运行吗为什么非要拆层如果只是写一个几十行的静态页面确实不需要复杂分层。但项目一旦进入多人协作或长期迭代阶段不分层会出现几种明显症状同一份按钮样式在多个页面文件中复制粘贴改样式要全局替换。组件之间的样式通过后代选择器互相污染A 页面的样式不小心影响了 B 页面。业务逻辑、模板结构和 UI 样式混在一起新人接手不知道从哪里改起。测试和复用困难组件无法脱离页面独立验证。分层系统要解决的就是这些问题。它让每个组件有明确的边界让依赖关系变得清晰让改动的范围可预估。1.3 分层系统适合哪些场景分层 HTML 组件系统不是大型前端框架的专利它适用于很多场景用原生 HTML/CSS/JS 开发的中后台页面或营销活动页。基于 jQuery 或原生 JavaScript 的旧项目重构。学习前端工程化之前的“组件思维”训练。需要长期维护、多人协作的静态页面项目。即使你正在使用 Vue 或 React分层思想同样成立。Vue 里的基础组件、业务组件、页面容器本质上就是一种分层。本文示例以原生 HTML/CSS/JS 实现目的是剥离框架干扰让你把注意力集中在“分层设计”本身。2. 环境准备与项目结构规划2.1 开发环境说明本文示例不依赖任何构建工具只需要满足以下条件即可运行。项目说明浏览器Chrome、Edge、Firefox 等现代浏览器语言HTML5、CSS3、原生 JavaScriptES6开发工具VS Code、WebStorm 等任意编辑器运行方式直接双击 HTML 文件或用 VS Code Live Server 启动如果你已经安装了 Node.js也可以使用npx serve启动静态服务但这不是必需条件。版本方面不需要刻意追新现代浏览器对 CSS 变量、语义化标签、Flex 布局的支持已经很成熟。2.2 推荐目录结构分层组件系统的目录设计应该能直接反映分层结构。下面的目录结构适合中小型项目project-root/ ├── assets/ │ ├── css/ │ │ ├── tokens.css // L0设计变量层 │ │ ├── reset.css // L0基础重置层 │ │ ├── components/ │ │ │ ├── button.css // L1基础按钮组件 │ │ │ ├── input.css // L1基础输入框组件 │ │ │ ├── badge.css // L1基础标签组件 │ │ │ └── card.css // L2卡片组合组件 │ │ └── pages/ │ │ └── home.css // L3页面级样式 │ ├── js/ │ │ ├── components/ │ │ │ ├── button.js // L1按钮逻辑 │ │ │ ├── dropdown.js // L2下拉组合逻辑 │ │ │ └── modal.js // L2弹窗组合逻辑 │ │ └── main.js // 页面初始化入口 │ └── images/ ├── index.html // 页面骨架 └── README.md // 组件使用说明目录中的tokens.css对应“设计令牌”概念也就是全局颜色、间距、字号等变量的统一存放位置。后续按钮、卡片、弹窗都从这个文件读取变量实现“改一处全局生效”。2.3 命名约定BEM 是分层的好搭档分层组件系统需要一套可预期、不冲突的命名规范。BEMBlock Element Modifier是目前最适合组件化开发的命名方式之一。BEM 的基本格式是.block {} .block__element {} .block--modifier {}解释一下block是独立的组件名例如.button、.card。__element表示组件内部的子元素例如.card__title。--modifier表示组件变体例如.button--primary。这套命名规范和分层思想天然契合每个组件是一个 Block内部子元素用 Element 表述状态变化用 Modifier 表达。后面所有代码示例都会遵循这一约定。3. 分层核心设计总览3.1 四层结构怎么划分为了说明清晰本文把分层系统拆成四层从底层到顶层依次为层级名称主要内容举例L0基础变量层CSS 变量、reset、全局工具类--color-primary、reset.cssL1基础组件层最小可复用 UI 单元按钮、输入框、标签L2组合组件层多个 L1 组件拼装搜索框、卡片、弹窗L3页面骨架层页面布局、区块组合首页、表单页、列表页每一层只能依赖它的下层禁止跨层依赖。例如 L2 的卡片组件可以引入 L1 的按钮和标签但 L1 的按钮不能去引用 L2 的卡片。L3 页面可以组合任意下层组件但不应该直接操作 L0 的底层样式变量以外的内容来实现复杂业务样式。3.2 分层带来的依赖收益当所有组件都遵守“下层不依赖上层”时项目会获得三个关键收益。第一修改范围可控。修改 L0 的全局主色所有引用该变量的组件同步更新不会遗漏。修改 L1 的按钮样式所有使用该按钮的组合组件和页面同步生效。第二复用成本降低。新页面需要某种交互时优先从已有组件中挑选组合而不是重新开发。时间越长组件库沉淀越多开发效率越高。第三测试和排查更简单。按钮变灰是 L1 的问题弹窗定位异常是 L2 的问题页面布局偏移是 L3 的问题。每一层错误都有明确的排查边界。3.3 分层系统的边界边界在哪里分层不是越细越好。如果每个标题、每段间距都拆成独立组件项目会变得难以维护。建议保持以下原则同一组件在页面中出现 2 次以上才考虑提取为基础组件。组合组件至少由两个不同职责的组件组成。页面级组件不要过度抽象页面本身就是业务场景最具体的表达。分层只是手段可维护性才是目的。4. 从零实现一个分层 HTML 组件系统下面通过一个完整的原生实现演示分层系统如何落地。示例会实现一个简单的“商品卡片 按钮 标签”页面所有代码均可直接复制运行。4.1 创建项目结构首先建立项目目录mkdir layered-html-components cd layered-html-components mkdir -p assets/css/components assets/css/pages assets/js/components创建完成后目录结构如下layered-html-components/ └── assets/ ├── css/ │ ├── components/ │ └── pages/ └── js/ └── components/4.2 L0设计变量层实现在assets/css/tokens.css中定义全局设计变量/* 文件路径assets/css/tokens.css */ :root { /* 颜色 */ --color-primary: #2f6fed; --color-primary-hover: #1d5cd8; --color-success: #22a06b; --color-warning: #d97706; --color-danger: #dc2626; --color-text: #1f2937; --color-text-secondary: #6b7280; --color-border: #e5e7eb; --color-background: #f9fafb; --color-white: #ffffff; /* 间距 */ --space-xs: 4px; --space-sm: 8px; --space-md: 16px; --space-lg: 24px; --space-xl: 32px; /* 字号 */ --font-size-sm: 12px; --font-size-base: 14px; --font-size-lg: 16px; --font-size-xl: 20px; /* 圆角 */ --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; /* 阴影 */ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08); /* 层级 */ --z-index-dropdown: 100; --z-index-modal: 1000; }这里把颜色、间距、字号、圆角、阴影、z-index 全部抽到变量里。后续所有组件都不直接写#2f6fed或16px而是引用变量。这样改主题色、调间距只需要改tokens.css。再看assets/css/reset.css它负责抹平不同浏览器的默认样式差异/* 文件路径assets/css/reset.css */ *, *::before, *::after { box-sizing: border-box; } html { -webkit-text-size-adjust: 100%; } body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, PingFang SC, Microsoft YaHei, sans-serif; color: var(--color-text); background-color: var(--color-background); line-height: 1.6; } img { max-width: 100%; display: block; } button, input, select, textarea { font: inherit; color: inherit; }4.3 L1基础按钮组件实现在assets/css/components/button.css中实现按钮组件/* 文件路径assets/css/components/button.css */ .button { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-sm); padding: 8px 16px; font-size: var(--font-size-base); font-weight: 500; line-height: 1.5; border: 1px solid transparent; border-radius: var(--radius-sm); cursor: pointer; transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; user-select: none; text-decoration: none; } .button:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; } .button:disabled { opacity: 0.6; cursor: not-allowed; } .button--primary { background-color: var(--color-primary); color: var(--color-white); } .button--primary:hover:not(:disabled) { background-color: var(--color-primary-hover); } .button--secondary { background-color: var(--color-white); color: var(--color-text); border-color: var(--color-border); } .button--secondary:hover:not(:disabled) { border-color: var(--color-primary); color: var(--color-primary); } .button--outline { background-color: transparent; color: var(--color-primary); border-color: var(--color-primary); } .button--outline:hover:not(:disabled) { background-color: var(--color-primary); color: var(--color-white); } .button--danger { background-color: var(--color-danger); color: var(--color-white); } .button--sm { padding: 4px 12px; font-size: var(--font-size-sm); } .button--lg { padding: 12px 24px; font-size: var(--font-size-lg); } .button--block { width: 100%; }这段样式展示了 BEM 的典型用法。.button是基础 Block.button--primary等是 Modifier控制不同场景的颜色和尺寸。按钮核心视觉统一由基础类负责变体只覆盖差异部分。再实现输入框组件assets/css/components/input.css/* 文件路径assets/css/components/input.css */ .input { display: block; width: 100%; padding: 8px 12px; font-size: var(--font-size-base); line-height: 1.5; color: var(--color-text); background-color: var(--color-white); border: 1px solid var(--color-border); border-radius: var(--radius-sm); transition: border-color 0.2s ease, box-shadow 0.2s ease; } .input::placeholder { color: var(--color-text-secondary); } .input:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(47, 111, 237, 0.15); } .input--disabled { background-color: var(--color-background); cursor: not-allowed; } .input--error { border-color: var(--color-danger); } .input--error:focus { box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.15); }再实现标签组件assets/css/components/badge.css/* 文件路径assets/css/components/badge.css */ .badge { display: inline-flex; align-items: center; padding: 2px 10px; font-size: var(--font-size-sm); font-weight: 500; line-height: 1.6; border-radius: 999px; background-color: var(--color-background); color: var(--color-text-secondary); } .badge--success { background-color: rgba(34, 160, 107, 0.1); color: var(--color-success); } .badge--warning { background-color: rgba(217, 119, 6, 0.1); color: var(--color-warning); } .badge--danger { background-color: rgba(220, 38, 38, 0.1); color: var(--color-danger); }4.4 L2组合组件实现组合组件由多个 L1 组件拼接。下面实现商品卡片它是典型的中台业务场景组件。新建assets/css/components/card.css/* 文件路径assets/css/components/card.css */ .card { background-color: var(--color-white); border: 1px solid var(--color-border); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); overflow: hidden; max-width: 360px; } .card__cover { width: 100%; height: 180px; object-fit: cover; background-color: var(--color-background); } .card__body { padding: var(--space-lg); } .card__title { margin: 0 0 var(--space-sm); font-size: var(--font-size-lg); font-weight: 600; } .card__desc { margin: 0 0 var(--space-md); font-size: var(--font-size-base); color: var(--color-text-secondary); } .card__meta { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-md); } .card__price { font-size: var(--font-size-xl); font-weight: 700; color: var(--color-danger); } .card__actions { display: flex; gap: var(--space-sm); border-top: 1px solid var(--color-border); padding-top: var(--space-md); }这里可以看到卡片组件内部同时使用了badge、button、card自身样式。L2 组件不关心按钮具体长什么样只关心布局如何组合。4.5 L3页面骨架实现页面骨架负责把 L1、L2 组件拼成最终的页面。新建index.html!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title分层 HTML 组件系统示例/title !-- L0设计变量与重置 -- link relstylesheet hrefassets/css/tokens.css link relstylesheet hrefassets/css/reset.css !-- L1基础组件 -- link relstylesheet hrefassets/css/components/button.css link relstylesheet hrefassets/css/components/input.css link relstylesheet hrefassets/css/components/badge.css !-- L2组合组件 -- link relstylesheet hrefassets/css/components/card.css !-- L3页面样式 -- link relstylesheet hrefassets/css/pages/home.css /head body main classhome-page h1 classhome-page__title精选商品/h1 p classhome-page__desc通过分层组件系统搭建的商品展示页/p div classhome-page__toolbar input classinput typetext placeholder搜索商品名称 button classbutton button--primary搜索/button /div div classhome-page__grid article classcard img classcard__cover srchttps://picsum.photos/360/180 alt无线耳机商品图 div classcard__body h2 classcard__title降噪无线耳机/h2 p classcard__desc支持主动降噪续航 30 小时多设备快速切换。/p div classcard__meta span classbadge badge--success热卖/span span classcard__price¥399/span /div div classcard__actions button classbutton button--primary button--sm加入购物车/button button classbutton button--outline button--sm查看详情/button /div /div /article article classcard img classcard__cover srchttps://picsum.photos/360/180?random2 alt机械键盘商品图 div classcard__body h2 classcard__title机械键盘/h2 p classcard__desc全键无冲热插拔轴体支持有线无线双模连接。/p div classcard__meta span classbadge badge--warning新品/span span classcard__price¥299/span /div div classcard__actions button classbutton button--primary button--sm加入购物车/button button classbutton button--outline button--sm查看详情/button /div /div /article article classcard img classcard__cover srchttps://picsum.photos/360/180?random3 alt智能手表商品图 div classcard__body h2 classcard__title智能手表/h2 p classcard__desc全天候心率监测支持多种运动模式续航 14 天。/p div classcard__meta span classbadge badge--danger限时/span span classcard__price¥899/span /div div classcard__actions button classbutton button--primary button--sm加入购物车/button button classbutton button--outline button--sm查看详情/button /div /div /article /div /main /body /html新建assets/css/pages/home.css放置只属于当前页面的布局样式/* 文件路径assets/css/pages/home.css */ .home-page { max-width: 1200px; margin: 0 auto; padding: var(--space-xl) var(--space-lg); } .home-page__title { margin: 0 0 var(--space-sm); font-size: 28px; } .home-page__desc { margin: 0 0 var(--space-lg); color: var(--color-text-secondary); } .home-page__toolbar { display: flex; gap: var(--space-sm); margin-bottom: var(--space-lg); max-width: 420px; } .home-page__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--space-lg); }4.6 运行与验证在项目根目录下用 Live Server 打开index.html或者直接在浏览器中双击index.html。预期效果页面顶部显示“精选商品”标题和副标题。搜索框和搜索按钮横向排列在标题下方搜索按钮使用主色样式。三个商品卡片以响应式网格排列在页面中。每个卡片包含封面图、标题、描述、标签、价格和操作按钮。鼠标悬停在按钮上时按钮颜色按照 hover 状态改变。如果看到图片没有加载不要担心picsum.photos是一个在线占位图服务本地网络受限时换成本地图片路径即可。例如把srchttps://picsum.photos/360/180改为srcimages/product-1.jpg。体验一下分层带来的效果把tokens.css中的--color-primary改为另一个色值保存刷新你会发现页面上所有主色按钮、聚焦边框、卡片文字颜色同步变化不需要手动修改任何组件样式文件。这就是 L0 变量的威力。4.7 JS 交互层设计分层组件系统不只是 CSS 层面的拆分JavaScript 交互同样需要分层。下面用一个简单的“加载按钮”案例说明。新建assets/js/components/button.js// 文件路径assets/js/components/button.js // L1基础按钮交互提供 loading 状态切换能力 (function () { function setLoading(button, isLoading) { if (!button || typeof button.dataset.loadingText undefined) { return; } const originalText button.textContent; if (isLoading) { button.disabled true; button.dataset.originalText originalText; button.textContent button.dataset.loadingText || 加载中...; } else { button.disabled false; button.textContent button.dataset.originalText || originalText; } } window.Button { setLoading, }; })();然后新建assets/js/main.js作为页面初始化入口// 文件路径assets/js/main.js // L3页面初始化逻辑负责把组件行为绑定到页面元素 (function () { const exampleBtn document.querySelector(#exampleBtn); if (!exampleBtn) return; exampleBtn.addEventListener(click, function () { window.Button.setLoading(exampleBtn, true); setTimeout(function () { window.Button.setLoading(exampleBtn, false); }, 2000); }); })();在index.html的按钮中加入>button idexampleBtn classbutton button--primary button--sm>script srcassets/js/components/button.js/script script srcassets/js/main.js/script这里分层逻辑是button.js是 L1 组件逻辑只负责“按钮自己如何从普通状态变成 loading 状态”main.js是 L3 页面入口决定“页面中哪个按钮在什么时机触发 loading”。L1 不关心页面业务L3 不负责按钮状态切换的底层实现。5. 常见问题与排查思路5.1 样式覆盖失败问题现象在页面级样式里想覆盖组件样式却发现没有生效或者被其他页面影响。可能原因没有控制 CSS 选择器优先级和文件加载顺序。解决思路先查看浏览器开发者工具F12中该元素的计算样式确认是哪条规则生效。检查页面级样式是否使用了与组件相同的选择器例如页面里写了.button {}这会影响所有按钮。页面级想要定制某个按钮优先使用组合类名例如.home-page__toolbar .button {}。不建议使用!important解决覆盖问题这会让后续维护者难以排查。5.2 CSS 变量没有生效问题现象按钮里引用的var(--color-primary)显示为无效值。可能原因tokens.css没有在 HTML 中引入或者变量声明位置不在:root。解决思路检查index.html中是否先引入了tokens.css。确认变量定义在:root或更上层的容器中子元素才能继承。检查变量名是否拼写一致CSS 变量是区分大小写的。在浏览器开发者工具中切换到 Computed 面板查看变量是否被解析。5.3 组件样式互相污染问题现象修改了某个组件的样式其他页面或模块也跟着变了甚至布局错乱。可能原因使用了全局后代选择器例如.card ul {}、.button span {}。解决思路坚持使用 BEM 命名避免通过标签选择器修改组件内部元素。页面级样式不要直接修改组件根类名而是将组件放在页面容器中再通过后代选择器覆盖。如果项目已经大量污染优先使用 CSS Modules、Scoped CSS 等隔离方案而不是逐一找原因。5.4 组件提取过度导致模板复杂问题现象页面被拆成很多层阅读模板时看不到业务完整流程修改一个字段需要跨多个文件。可能原因过度抽象把只需要出现一次的布局也做成了组件。解决思路遵循“出现两次以上才提取”的原则。组合组件如果只服务于特定页面可以放回页面目录而不是进入通用组件目录。分层系统的目标是降低维护成本如果分层本身让维护变难就要减少分层深度。5.5 JS 事件绑定不生效问题现象按钮点击后没有任何反馈控制台报错提示找不到元素。可能原因脚本在 DOM 加载前执行或元素选择器写错。解决思路将script放在/body前或监听DOMContentLoaded事件。使用浏览器开发者工具确认元素是否存在于页面中。检查id是否重复ID 在页面中必须唯一。6. 最佳实践与工程化建议6.1 命名规范统一分层系统的第一步就是统一命名。CSS 类名推荐使用 BEMJavaScript 暴露的组件对象推荐统一挂载到window下的命名空间。// 推荐写法 window.Layered window.Layered || {}; window.Layered.Button { setLoading }; // 避免这样 window.buttonLoading function () {};统一命名空间的好处是页面脚本不会意外覆盖组件逻辑组件之间的调用入口一目了然。6.2 设计令牌优先颜色、间距、字体、阴影、圆角这类高频值必须统一放进tokens.css。不要因为“只有一次”就直接写死。理由很简单没有变量时设计改版会让全局搜索替换变成噩梦。常用的设计令牌包括颜色类主色、成功色、警告色、危险色、中性色。间距类xs、sm、md、lg、xl 五档。字号类sm、base、lg、xl 四档。圆角类sm、md、lg 三档。阴影类轻阴影、中阴影。层级类dropdown、sticky、modal、toast。6.3 每层只做好自己的事这里是一条看似简单但很难坚持的原则组件文件不要写页面布局页面文件不要写全局变量。如果页面布局里发现需要某个全局变量把它提升到tokens.css如果组件里发现要感知具体页面元素应该通过接口参数传入而不是在组件内部写死。JS 侧同样如此。组件脚本通过公开方法暴露能力页面脚本负责调用。组件不要自己去监听与自身无关的全局事件。6.4 可访问性与语义化分层组件系统在视觉之外还要关注无障碍体验。按钮和链接语义要区分执行动作用button页面跳转用a。表单控件必须关联 label不能只放一个placeholder。图标按钮要提供aria-label描述。弹窗要使用roledialog、aria-modaltrue并管理焦点。图片必须添加alt属性装饰性图片使用alt。下面的代码展示了搜索框的语义化写法div classhome-page__toolbar label classvisually-hidden forsearchInput搜索商品/label input classinput idsearchInput typetext placeholder搜索商品名称 button classbutton button--primary typebutton搜索/button /div其中.visually-hidden可以这样定义.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }6.5 组件文档与示例页当组件数量变多以后维护者最头疼的是“不知道项目里有哪些组件、怎么用”。建议在项目中维护一个独立组件展示页例如components.html每个组件配上示例代码和使用说明。不需要复杂工具一个静态 HTML 就能完成。示例页的结构components.html ├── 按钮组件示例primary / secondary / outline / danger ├── 输入框组件示例normal / disabled / error ├── 标签组件示例success / warning / danger └── 卡片组件示例商品卡片 / 用户信息卡片组件展示页既是文档也是开发联调时的手动测试区域。6.6 渐进增强与性能注意在分层架构下性能优化可以分视角处理。L0 层要控制样式文件体积变量定义不要冗余避免同一个含义的颜色出现多份定义。L1 层要注意不要堆砌选择器嵌套深度。虽然 CSS 可以嵌套但解析规则越深性能损耗越高更重要的是维护困难。推荐选择器层级控制在 3 层以内。L2 层要注意组合组件不要过度包裹否则会生成一堆无语义的div既影响开发者阅读模板也影响页面语义。适当使用article、section、header等语义标签替代无意义容器。L3 层要关注图片和静态资源加载商品卡片图片建议开启懒加载img loadinglazy classcard__cover src... alt...7. 总结与后续方向这一个完整的分层 HTML 组件系统示例核心知识可以总结为四点。第一分层的关键是依赖方向上层依赖下层下层不依赖上层。L0 设计变量、L1 基础组件、L2 组合组件、L3 页面骨架每一层职责清晰。第二CSS 变量是分层系统的粘合剂。设计令牌让所有组件共享同一套视觉基础改一处即可全局生效。第三BEM 命名让样式可预期、可定位。组件类名和页面类名互不干扰新增页面不必担心污染已有样式。第四原生 HTML/CSS/JS 完全能承载分层设计。本文示例不依赖任何框架说明分层首先是一种工程思维不完全是工具链问题。往后你可以尝试两个方向一是引入现代工具链。把tokens.css改为 SCSS 变量或 PostCSS 方案把原生组件封装成 Vue/React 组件在保留分层思想的同时提升开发效率。二是为项目补充自动化测试。L1 基础组件可以用 Storybook 做可视化测试L2 组合组件可以用 Playwright 做端到端测试。分层之后每个层级的测试边界也变得清晰。如果这篇教程对你有帮助可以收藏备用。需要实际操作时建议从 L0 和 L1 开始搭建先跑通一个按钮组件再扩展到卡片、弹窗等复杂场景。分层的价值只有在长期维护中才会越来越明显动手写一版你会更有体感。