那一天,一个按钮把我逼成了“依赖图侦探”。
最糟糕的前端事故,不是页面崩了。而是页面看起来一切正常,却在某个角落悄悄“不听话”——用户点了,你装作没看见。
我们上线了一个看似无害的 UI 小改动,结果结账按钮开始对一部分用户“选择性失明”:点了没反应。不是所有人,不是所有浏览器,只是刚好够多——多到客服工单像抽签一样随机,还带点私人恩怨的味道。
最后发现问题根本不在业务逻辑。真正的“罪魁祸首”藏在依赖关系里。
那次升级动了 900 条 lockfile 记录。组件本身只有八行 UI。这个荒诞的比例,直接把我整破防了:我们已经不是在调一个组件了,我们是在调一个脚下随时会塌的“移动平台”。
也是从那一周开始,我不再纠结“哪个框架更强”。我开始问一个更现实的问题:我们的 UI,有哪些部分能扛得住下一次重写?
浏览器已提供的契约
前端框架当然厉害,能让你飞快交付产品。但它的费用不是当场结算,而是晚点以“折腾”的形式找你收——一年一个新路由,一年一个新渲染模式,一年一个新构建工具。真正拖垮人的,从来不是 React、Vue 或 Angular 本身,而是那张不断上涨的迁移税单:你明明只是想让 UI 老老实实做个“配角”,结果它天天抢戏。
Web Components 改变的是游戏规则,因为它的合同对象是浏览器。
一个自定义元素,本质上就是一个标签。输入靠 attributes 和 properties。输出靠 DOM 事件。这套契约不在乎你宿主是谁。
你可以把同一个组件塞进服务端渲染页面、老旧应用、或现代单页应用里。组件不会追着你要状态库,也不会逼你换工具链。它只问你一句:“给我 HTML 就行。”
在真实代码库里,“赢”到底长什么样
Web Components 真正让人觉得爽,是当你需要把同一套 UI 发到那些永远达不成一致的地方。
我们同时有:营销站、内部后台、合作方嵌入组件。三个仓库、三套构建规则、三种“祖传习惯”。我们同一个小部件硬生生重写了三遍,最后还收获了三种略有差异的 bug——你说气不气。
后来我们把它做成一个 web component。只做一次。第一次在三个地方都正常运行的瞬间,你会明显感觉肩膀松下来,构建系统也终于不再对你吼叫。
App Or Cms Or Dashboard
|
v
`<x-pay></x-pay>`
|
Shadow Dom Boundary
|
Styles And Markup
从那以后,框架变成了实现细节。标签本身才是产品。
一个“表面干净”的组件
原则很简单:对外接口越小越好,复杂度尽量关进门里,用事件沟通。
<x-toggle on="0"></x-toggle>
<script>
class XToggle extends HTMLElement {
static get observedAttributes() { return ['on'] }
connectedCallback() {
const r = this.attachShadow({mode:'open'})
r.innerHTML = `<button id=b></button>`
r.getElementById('b').onclick = () => {
const v = this.getAttribute('on') === '1' ? '0' : '1'
this.setAttribute('on', v)
this.dispatchEvent(new CustomEvent('change', {detail:v}))
}
this.render()
}
attributeChangedCallback() { this.render() }
render() {
const b = this.shadowRoot.getElementById('b')
b.textContent = this.getAttribute('on') === '1' ? 'On' : 'Off'
}
}
customElements.define('x-toggle', XToggle)
</script>
把这个标签丢进任何应用里就能用。监听 change 事件。设置 on 属性。就这么多。没有那种“今天能跑、明天就烂”的包装层。
这也是团队开始感到轻松的地方:你不再为了“最佳实践”吵个没完,而是围绕契约达成共识——大家终于讲同一种语言。
为什么它们现在终于开始赢了
Web Components 不是新东西。新的是:周边条件终于成熟了。浏览器更稳定了。模块化成了默认选项。团队也被“移动部件”折磨到疲惫了。
当然,框架依然有它的统治区:复杂导航、重状态管理、富客户端交互。但 Web Components 的强项很明确:设计系统、共享组件、跨边界嵌入的小部件。
它们不是想取代一切。它们更像是在做一件更务实的事:先把出血点止住。
最后
选择 Web Components,意味着你更贴近平台。而平台从来不温柔。
主题化要提前设计。Shadow DOM 的样式隔离有时会反咬你一口。无障碍是你的责任,尤其是焦点管理和键盘交互。服务端渲染也需要你有意识地做整合。没有一项是“做不了”,但每一项都是真工程,不会凭空自动发生。
下一波框架浪潮再来,我们的核心 UI 不会惊慌失措。它就像一个标签,安安静静待在那里,继续把自己的事做好。
这次从“依赖图侦探”到拥抱平台原生能力的探索,让我对技术选型的长期价值有了更深理解。在云栈社区的讨论中,我们也发现越来越多的团队开始重新审视并应用 Web Components 来解决跨栈、跨版本的组件复用难题。