Skip to content

投影:Shadow DOM 与 iframe ​

用户看到的 DOM,不一定是简单 document.querySelector() 遍历到的 DOM。Shadow root 形成组合树;同源 iframe 形成另一个拥有独立视口与坐标空间的 document。投影层的任务,是让动作、区域、命中测试、滚动和可见性对“用户实际看到的是哪个元素”达成一致。

投影改变了什么 ​

在 open shadow root 内,元素仍属于当前 document,但父链会穿过 ShadowRoot 到宿主。查询必须走组合树;可见性与遮挡必须按顶层视口计算;宿主上的 v-scope 仍应作为内部声明的祖先。

同源 iframe 的内部 document 有自己的全局对象与布局。父页面必须把运行时显式共享给指定 frame,不能因为浏览器允许访问就把内部内容自动并入。坐标要经过 frame 盒模型映射,并在支持范围内考虑 CSS transform。

支持的边界 ​

边界行为
Open shadow root组合树查询;宿主作用域可见
Closed shadow root不穿透
显式共享的同源 iframe共享注册表与投影坐标
跨域 iframe不穿透
多个框架岛各自适配器;namespace 防止 id 冲突

框架刻意不扫描所有 iframe,也不打开所有 shadow root。显式共享让所有权和生命周期可见。复用父运行时的子页面仍要声明自己的动作,并避免覆盖无关 id。

几何也是正确性的一部分 ​

一个元素可以存在、已启用,却仍不可达:粘性页头遮住它,frame 被裁剪,或滚动容器已经把它移出视口。因此,可用性不是一次选择器匹配。投影层要把元素在组合树中的位置与顶层视口、相关滚动链一起检查。

这也是为什么按钮在单元测试中能调用事件,不代表真实用户能点到。执行器应报告遮挡或视口外状态,而不是静默宣称交互成功。

命名空间不是安全边界 ​

两个框架岛可以声明同名局部动作。命名空间会把 draft 变成 react/draft 或 vue/draft,避免一个岛的执行器误跑另一个岛的声明。它解决冲突,不授予权限。当前用户会话、委托范围与业务后端仍决定谁能执行该操作。

同理,iframe 边界不自动等于业务数据的安全边界。父页面显式共享运行时后,子页面可以成为同一能力面的一部分。若内容不可信,就不应共享运行时,应在 origin 与应用边界隔离。

测试投影 ​

对 shadow-root 组件,验证嵌套动作继承宿主区域、命中测试能找到正确按钮、内部弹窗能挡住后方动作。对 iframe,验证显式共享、不同尺寸下的坐标映射、frame 内滚动,以及未共享时的失败。

不要只直接调用框架 API。要按模型或外部 Agent 的路径测试:描述页面、调用可见能力、检查业务状态。投影问题往往只有把选择、几何和执行放在一起时才暴露。

适配器 API 与已知限制见 连续性与投影。