写过的人都踩过这个坑——每个组件库都要加前缀防冲突,sl-button、md-dialog 今天 Scoped Custom Element Registries 把这件事彻底原生化了

写过前端架构的人都踩过这个坑——组件库多了,每个都要加前缀防冲突,sl-button、md-dialog、wa-card,今天 Scoped Custom Element Registries 把这件事彻底原生化了。

这件事的根子在哪?所有自定义元素都得走同一扇门:customElements.define('user-card', UserCard),整个页面只认这一个全局 registry。名字冲突是致命的——两个库同时定义同一个标签名,第二个 define() 直接抛 NotSupportedError,没有任何补救的余地,页面直接崩掉。

结果就是:前缀成了标配。sl-buttonmd-dialogwa-dialog——这些前缀从来不是给语义加分的,只是唯一可靠的防撞墙。更要命的是,你没法在同一个页面跑同一组件库的两个版本。设计系统从 v1 迁到 v2,两个版本都注册了同名标签,只有一个人能赢。

第三方嵌入 Widget 更是高危操作。谁知道宿主页面注册了什么名字?Widget 里的自定义元素随时可能和宿主或其他 Widget 的标签名冲突。

这个问题,Shadow DOM 早就帮样式和 markup 解决了,唯独碰不了元素名本身。现在 Scoped Custom Element Registries 补上了最后一块短板。

解决方案:每个 ShadowRoot 有自己的 registry

平台加了两个 API:

“`js
// 1. 自己造一个 registry,不再受限于 window.customElements
const registry = new CustomElementRegistry();
registry.define(‘product-card’, ProductCard);

// 2. attachShadow 时绑定这个 registry
const shadow = host.attachShadow({
mode: ‘open’,
customElementRegistry: registry,
});

// 3. 这个 shadow 树里解析 <product-card> 用 registry,不是全局 registry
shadow.innerHTML = ‘<product-card></product-card>’;
“`

同一个页面上,两个 shadow 根各自注册了同名 <scoped-button>,互不干扰,各自渲染成完全不同的实现。

浏览器支持现状

Chrome 和 Safari 已经在稳定版里支持了,Firefox Nightly(156)刚刚接上,还不是稳定版。一旦 Firefox 稳定版发出来,三大引擎全部覆盖,Scoped Custom Element Registries 就正式进入 Baseline。

Polyfill 每周有 2.4 万次下载,说明这个需求不是小众——从 Web Components 诞生那天起,开发者就在等这个能力。

下一步

按有意义的边界来划分作用域,不要给每个小组件都单独建 registry——在 App Shell、Feature Island 或第三方 Widget 层级做隔离,保持依赖图可维护。渐进增强策略:先 feature-detect 支持情况,不支持的浏览器保留全局 registry fallback。

“`js
// 渐进增强写法
if (‘CustomElementRegistry’ in window &&
typeof CustomElementRegistry === ‘function’) {
const registry = new CustomElementRegistry();
registry.define(‘my-button’, MyButton);
host.attachShadow({ mode: ‘open’, customElementRegistry: registry });
}
“`

微前端、多团队组件库、第三方嵌入——这三个场景以后不需要靠前缀活着了。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 11 阅读