前言
很多公司吹嘘自己有“自研组件库”,点开一看,其实就是把 Ant Design 的按钮改了个颜色,再套个壳。
真正的物料基建,不是为了解决“按钮长什么样”,而是为了解决“为什么我的项目里有 18 个逻辑一模一样的搜索框,且没一个能直接复用”。如果你的基建不能让业务开发在面对 PM 的奇葩需求时少写 50% 的代码,那它就是个摆设。
一、 认知突围:物料是“业务逻辑”的载体
在架构师眼中,物料的治理不应停留在 UI 视觉层,而应深入到业务语义层。
1.1 从“工具包”到“资产库”
-
UI 组件 (Low Level): 解决的是“样式统一”。(如:Modal, Select)
-
业务物料 (High Level): 解决的是“行为统一”。
- 例子: “用户选择器”不仅是一个下拉框,它背后关联着:接口鉴权、防抖搜索、分页加载、头像渲染。
- 深度治理: 如果每个业务线都自己写一遍这套逻辑,那就是 10 倍的维护成本。
1.2 为什么大部分物料库会走向“腐烂”?
- 过度封装: 为了支持所有场景,给一个组件开了 50 个 Props,最后代码里全是
if/else。 - 文档滞后: 开发者看文档像是在猜灯谜,最后发现“看源码比看文档快”。
- 版本割裂: 核心库升级了,业务线不敢升,最后全公司跑着 5 个版本的组件库。
二、 深度工程化:物料的“生产流水线”
要让物料体系真正流转起来,架构师必须构建一套**“非人治”**的自动化链路。
2.1 基于 AST 的“自动化元数据提取”
别再让开发者手写文档了。利用 TypeScript 的编译器 API(Compiler API),在物料发布时自动扫描源码:
- 自动提取 Props 定义、注释、默认值。
- 自动生成 API 表格。
- 自动识别依赖项。
- 意义: 确保“代码即文档”,从根源上消灭文档与代码不一致的问题。
2.2 视觉回归测试:基建的“保险杠”
在企业级治理中,你最怕的就是:改了 A 组件的一个边距,结果 B 业务线的老页面直接塌陷了。
- 方案: 引入 Visual Regression Testing(如 Playwright + Pixelmatch)。
- 实战: 在 CI 环节,自动化对比组件修改前后的像素差异。哪怕只是偏移了 1px,也要在 PR 阶段被拦截。
三、 治理逻辑:如何让物料“好找且敢用”
3.1 建立“物料索引市场” (Discovery System)
如果一个物料不能在 30 秒内被开发者搜到,那它就不存在。
- 智能搜索: 不止搜名称,更要搜“功能描述”。(搜“上传照片”,能关联出“图片裁剪”和“头像上传”)。
- 在线 Sandbox: 必须提供即时预览和代码试运行。开发者应该在“买”之前,先在浏览器里把玩一下。
3.2 影子测试与灰度策略
核心物料升级时,利用 Babel 插件或 Webpack 插件,在编译阶段分析业务代码的覆盖情况。
- 深度实践: 统计哪些业务方使用了该物料的哪些属性。如果某属性没有任何人用,直接在下一版本废弃(Deprecate),保持物料库的“轻盈”。
四、 架构师的权衡:标准化 vs 灵活性
这是一个经典的架构陷阱:物料封装得越死,复用性越高,但灵活性越差。
4.1 经典陷阱:“千手观音”组件
想象一下,你们团队需要一个“开关 (Switch/Toggle)”组件。
起初(标准化阶段): 基础架构组设计了一个极其标准的 <StandardSwitch />。它只有两个属性:checked 和 onChange。样式是写死的:圆角、蓝色背景。大家用得很开心,规范统一。
后来(灵活性需求爆发):
- 业务线 A:我们的产品主色调是红色,能改颜色吗?
- 业务线 B:我们要搞促销活动,这个开关得是方形的,里面还要加个文字图标。
- 业务线 C:我们需要把开关放在一个极小的空间里,尺寸能自定义吗?
结果(架构腐化): 为了满足这些需求,<StandardSwitch /> 被迫增加了几十个 Props:color, borderRadius, size, showIcon, iconContent...
最终,这个组件变成了一个长着无数只手的“千手观音”,内部充斥着复杂的样式判断逻辑,维护成本极高,且性能堪忧。
4.2 破局思路:Headless (无头) 组件
Headless 的核心思想是:将“逻辑的脑子”与“渲染的皮囊”彻底分离。
- 有头组件 (Traditional): 买电脑送显示器。你想换个 4K 屏?对不起,主机和屏幕焊死在一起了。
- 无头组件 (Headless): 只卖主机。你爱接 4K 屏、带鱼屏还是投影仪,随你便。
架构图解:分离的艺术
我们来看下 Headless 模式下,组件的分层架构:
如上图所示:
- 底层 (Headless 逻辑层): 封装了所有“脏活累活”。比如,开关的状态切换、按空格键触发切换、盲人阅读器的
aria-checked属性支持等。这些逻辑是通用的,与 UI 无关。 - 顶层 (UI 渲染层): 完全由业务方自己决定。他们可以使用
<div>,<span>, CSS-in-JS, Tailwind CSS,想画成圆的就画成圆的,想画成方的就画成方的。
4.3 代码实例:从“千手观音”到“灵活组装”
我们用 React Hooks 来演示一下这个转变(Vue 的 Composition API 同理)。
场景:实现一个 Switch 开关
1. 定义 Headless Hook (只管逻辑):
这个 Hook 包含了开关的所有核心能力,但不涉及任何 DOM 和 CSS。
// useSwitch.ts (物料库提供)
import { useState, useCallback } from 'react';
export function useSwitch(initialState = false) {
// 1. 状态管理
const [isOn, setIsOn] = useState(initialState);
// 2. 交互逻辑
const toggle = useCallback(() => setIsOn(v => !v), []);
// 3. 辅助功能 (A11y) 属性生成器
const getSwitchProps = () => ({
role: 'switch',
'aria-checked': isOn,
tabIndex: 0,
onClick: toggle,
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
toggle();
}
}
});
// 只返回状态和逻辑方法
return { isOn, toggle, getSwitchProps };
}
2. 业务方 A 的实现(标准圆角蓝风格):
业务方拿到了逻辑,自己决定怎么渲染。
// BusinessA_Switch.jsx (业务方 A 自定义)
import { useSwitch } from '@my-org/hooks';
import styled from 'styled-components';
const StyledButton = styled.button`
background-color: ${props => props.isOn ? 'blue' : 'gray'};
border-radius: 9999px; // 圆角风格
// ... 其他样式
`;
export function StandardSwitch() {
// 使用 Headless 能力
const { isOn, getSwitchProps } = useSwitch();
return (
// 将逻辑属性解构赋值给 UI 元素
<StyledButton isOn={isOn} {...getSwitchProps()}>
<span className="thumb" />
</StyledButton>
);
}
3. 业务方 B 的实现(方形红色促销风格):
业务方 B 可以完全复用逻辑,画出截然不同的 UI。
// BusinessB_PromoSwitch.jsx (业务方 B 自定义)
import { useSwitch } from '@my-org/hooks';
export function PromoSwitch() {
const { isOn, getSwitchProps } = useSwitch();
return (
// 使用 Tailwind 编写完全不同的方形样式
<div
{...getSwitchProps()}
className={`${isOn ? 'bg-red-500' : 'bg-zinc-300'} w-16 h-8 rounded-none flex items-center cursor-pointer`}
>
<div className="bg-white w-6 h-6 mx-1 rounded-none">
{isOn ? '开' : '关'}
</div>
</div>
);
}
4.4 总结
通过 Headless 模式,架构师完成了对权力的完美让渡:
- 架构师守住了底线: 核心交互逻辑、状态流转、可访问性标准被统一封装,不会因为业务方的 UI 定制而产生逻辑 Bug。
- 业务方得到了自由: 他们再也不用为了改个颜色而去求基础架构组加 Props 了。
这就是那句格言的深层含义:
“给业务方留一扇窗(UI 自定义能力),他们就不会想拆掉你的墙(核心逻辑封装)。”
五、 总结:从“重复造轮子”到“按需组装”
5.1 研发复利:架构师的“长期主义”
在一般的团队中,工作量是随项目数量线性增长的;而在拥有顶级物料治理的团队里,工作量曲线应该是对数级的。
- 初期(高投入): 你可能花了 2 周才磨合出一个完美的、Headless 架构的“财务大搜表”物料。
- 后期(高回报): 当公司要开 5 个新的后台管理页面时,开发者只需要花 10 分钟引入物料并配置 Schema。由于物料已经在 100 个场景下跑过,其**健壮性(Robustness)**是任何新写的代码都无法比拟的。
5.2 案例对比:从“冷启动”到“一键飞升”
我们来看一个实际的业务场景:实现一个带“权限控制”和“自动重试”功能的图片上传组件。
| 维度 | 传统“造轮子”模式 (No Infrastructure) | “按需组装”模式 (Material Asset) |
|---|---|---|
| 开发耗时 | 3 - 5 小时(找文档、调 API、写逻辑、调样式) | 5 - 10 分钟(拖拽组件,填写 API Key) |
| 代码量 | 150+ 行(逻辑散落在各个组件中) | 3 行(纯声明式配置) |
| 稳定性 | 极低(不同人写的代码,异常处理逻辑不一) | 极高(物料自带熔断、重试、OSS 分片逻辑) |
| 可维护性 | 噩梦(后端 API 一改,全项目全局搜索替换) | 轻松(物料中心统一升级,全线同步生效) |
- 向上管理: 用数据告诉老板,你搞的基建到底省了多少钱(换算成工时)。
- 向下优化: 如果某个物料的使用率为 0,说明要么是不好用,要么是没推广,架构师应及时止损,将其踢出资产库。
结语:交付的最后一公里
研发效能不是看你写代码有多快,而是看从“代码提交”到“用户可用”的总时长(Lead Time)。
1. 现状对比:手工业 vs. 工业化流水线
如果交付链路不打通,你的基建就像是在泥潭里开跑车。
| 环节 | 人肉运维 (手工时代) | DevOps 交付 (架构时代) |
|---|---|---|
| 构建 | 开发者在本地执行 npm build,环境不一致导致“我本地明明是好的”。 | 云端环境统一构建(Docker 镜像),保证 100% 环境一致。 |
| 部署 | 找运维开权限,手动 FTP 上传或 SSH 到服务器执行 git pull。 | 合入即部署。代码通过测试自动触发部署,开发者无需关注服务器。 |
| 配置 | 手动修改 Nginx 转发规则、配置跨域、刷新 CDN 缓存。 | 配置即代码 (IaC) 。Nginx 和路由配置随代码版本走,一键生效。 |
| 回滚 | “快!把刚才那个备份压缩包覆盖回去!”(手忙脚乱中可能覆盖错版本)。 | 秒级回滚。通过镜像版本切换,一键回到任何稳定时刻。 |
2. 图解:交付链路的“能量损耗”
我们可以用一个“水管模型”来理解交付瓶颈:
警示:
如果中间那段“交付管道”是细窄的、堵塞的,那么你前端基建做得再大(漏斗再宽),最终流向用户的价值流速依然由那根细管子决定。