背景
最近在开发 AutoForm SDK 时,为了实现"一次开发,到处运行",我们选择了 Web Component 技术方案。
本以为是银弹,结果踩了一路的坑。今天把这些血泪经验总结出来,希望能帮大家少走弯路。
坑一:样式隔离太彻底了
Shadow DOM 的初衷是隔离样式,防止污染。但有时候我们想让外部样式影响内部,比如让用户自定义字体。
现象:宿主页面设置了 body { font-family: 'Roboto' },但 SDK 里的字体还是浏览器的默认字体(Times New Roman)。
解决方案:
- CSS 变量穿透:这是最推荐的方式。
/* SDK 内部 */ :host { font-family: var(--af-font-family, sans-serif); } ::part伪元素:允许外部选择 Shadow DOM 内部标记了part的元素。<!-- SDK 内部 --> <button part="submit-btn">提交</button>/* 外部 CSS */ autoform-widget::part(submit-btn) { background: red; }
坑二:React 的合成事件失效
这是最经典的一个坑。React 16 及以前版本,把事件代理到了 document 上。
现象:在 React 项目中引入我们的 SDK,点击 SDK 里的按钮,React 的 onClick 根本不触发。
原因:事件从 Shadow DOM 冒泡出来时,target 被重定向到了 Host 元素。React 发现 target 变了,或者事件在 Shadow Boundary 被截断了,就无法正确分发。
解决方案:
- 升级 React 17+:React 17 改为将事件代理到 Root 节点,缓解了部分问题。
- 手动派发事件:在 SDK 内部,手动 dispatch 一个
composed: true的自定义事件。
坑三:弹窗被 overflow: hidden 截断
如果你的 Web Component 放在一个 overflow: hidden 的容器里,而你又想弹出一个超出容器的 Modal 或 Tooltip,你会发现它被截断了。
原因:Shadow DOM 并没有创建一个新的层叠上下文(Stacking Context)来逃离父级的 overflow 限制。
解决方案:
使用 React Portal 或类似的机制(Svelte 的 Portal),将 Modal 渲染到 document.body 下,而不是 Shadow DOM 内部。
但这样又会带来新问题:渲染到 body 下的元素失去了 Shadow DOM 的样式隔离保护!
终极方案: 创建一个新的 Web Component 专门用来放 Modal,并将其挂载到 body 下。
总结
Web Component 虽然标准很美好,但在实际工程中,特别是与现有框架(React/Vue)混用时,还是有很多细节需要注意。
希望这些经验能帮到正在折腾 Web Component 的你。
👉 官网地址:51bpms.com