Skip to main content

样式和脚本隔离

icestark 当下的方案里,无论是主应用还是微应用都是直接在页面里执行,本质上不存在隔离机制,针对这个问题我们一方面通过一些规范来保证污染问题,一方面也在尝试更加彻底的沙箱机制,如果你的微应用都是二方接入,那我们推荐直接通过规范约束即可,如果存在三方接入这种不可控的场景,建议还是通过 iframe 的方式嵌入。

样式污染#

页面运行时同时只会存在一个微应用,因此多个微应用不存在样式相互污染的问题,但是主应用和微应用是同时运行的,因此这两者中间可能会存在一些样式相互污染,针对这个问题,我们目前推荐「通过约定避免微应用与主应用的样式相互污染」的方案,同时也在尝试 Shadow DOM 的方案。

规范#

使用 CSS Modules 方案管理样式#

无论是主应用还是微应用,直接通过 CSS Modules 的方案管理自身可控的样式,这样基本杜绝了两者样式冲突的问题。

主应用自定义基础组件 prefix#

除了自身可控的样式,应用中还会有一些全局样式,比较典型的就是类似 next 这种基础组件的样式,如果主应用和微应用使用了不同版本的 next,则很容易造成样式相互污染,这种场景推荐在主应用中将基础组件的前缀统一改掉,比如将 next- 改为 next-icestark-,这个能力已在主应用模板中内置,具体可参考相关代码。

微应用避免产生全局样式#

对于类似 normalize.css 这种全局重置样式,推荐统一通过主应用引入,微应用尽量避免产生全局性质的样式,因为这样在切换微应用时可能会因为全局样式差异产生一些抖动。

Shadow DOM(方案试验中)#

如果将微应用渲染到 Shadow DOM 中,那么微应用产生的所有样式都不会污染到全局,事实上在我们试验的过程中的确是这样的。但是我们遇到一个当下无法解决的问题,大部分类似 Dialog 组件的实现都是在 body 下创建一个容器节点,但是 Shadow DOM 里 Dialog 的样式无法作用到全局,因此展示出来 Dialog 就是无样式的,在这个问题上我们还在尝试,比如类似 Dialog 组件的实现能够进行优化:判断自身是否在 Shadow DOM 里,如果是的话则将容器节点创建到 Shadow DOM 里,否则创建到 body 节点下。

JS 污染#

相对于样式污染,JS 污染的危害性更高,在目前的方案下,如果微应用想要恶意污染的话基本是无法杜绝的,因此针对这种不可控的微应用建议还是通过 iframe 的方式接入。针对可控的二方应用,正常书写代码是不会有问题的,针对一些特殊情况我们也总结了一些规范。

规范#

微应用避免改变全局状态#

比如改变全局变量 window/location 的默认行为,通过 document 操作 Layout 的 DOM,这些本身都是一些不推荐的做法。

主应用通过钩子记录并恢复全局状态#

<AppRouter
onAppEnter={(appConfig) => {
// 按需记录全局状态
}}
onAppLeave={(appConfig) => {
// 按需恢复全局状态
}}
>
// {...}
</AppRouter>

基于 Proxy 的运行沙箱#

通过 with + new Function 的形式,为微应用脚本创建沙箱运行环境,并通过 Proxy 代理阻断沙箱内对 window 全局变量的访问和修改。

icestark 内置了基于 @ice/sandbox 的沙箱隔离,通过 sandbox 属性开启:

<AppRoute
sandbox
path="/seller"
title="商家平台"
url={[
'//unpkg.com/icestark-child-seller/build/js/index.js',
'//unpkg.com/icestark-child-seller/build/css/index.css',
]}
/>