Description
背景
文档的外部壳和内嵌 demo 的样式表会进行合并且同时在一个 runtime 中被渲染,这意味着,倘若文档外部的壳中有部分全局样式,则会污染到内嵌 demo 的样式,导致开发者在预览阶段看到的效果和生产使用时未必一致,后果十分严重。
备选方案
隔离无非 3 种方案:
iframe 天然隔离
从 Web 层面上来说它似乎就是最佳实践,但在开发 & 使用体验上它却是比较糟糕的:
- 我们从浏览器控制台中无法直接看到 iframe 抛出的错误
- 倘若我们演示的是 Modal 类型的 demo,这个 demo 在 iframe 中想必会『难以自拔』
ShadowDOM 特性隔离
这是最开始想优先考虑的方案,但在请教 @ikobe 后了解到这里的水非常深。
当一个节点成为 ShadowDOM 后,它的样式表需要手动注入到自己的节点下,这里需要经历编译 demo -> 分析 demo 所引用的全部样式表 -> 编译样式表 -> 存储样式表(也可以字符串变量存在组件中)-> 在运行时插入样式表,目前 webpack 提供的构建钩子中,虽然有能分析到依赖的钩子,但已经处于无法修改构建产物的阶段,如果要继续往这条没人走过的路上走,可能会非常黑,而且不利于后期维护。目前思考下来有如下两个方向还存有突破的可能性:
- 在 style-loader 层面做捕获,修改 insertStyle 的行为,但仍然需要看 style-loader 能不能输出 demo 与样式表的依赖关系(尚未调研);
- 利用子进程对每一个 demo 做一次单独的构建到 VFS,拿到 css 产物后再回到主进程继续编译 Page Component,可行性应该是没问题,但 demo 一多性能是肯定需要考虑的问题。
为 demo 规避污染源,主动隔离
这是现在 ant.design、Docz 等采用的方案。
原理十分简单,规范外部文档壳样式的编写及应用方式,不写全局样式,对局部样式做大容器的包裹,比如对 markdown 的样式区域用 .markdown
的 className 包裹,这样 demo 区域的样式就不会受到污染。但也存在一些弊端:
- 虽然外部文档壳样式不会污染 demo,但倘若 demo 中有全局样式,仍然会污染外部的文档壳;
- 倘若同一个页面的两个 demo 之间有样式冲突,也会出现污染。
最终结论
分析下来,从工程角度上讲,最完美的方案肯定是 ShadowDOM;从 ROI 的角度上讲,最适合的方案肯定是主动隔离;至于 iframe,则让它去吧。
考虑到目前 father-doc 正处于 0-1 的阶段,先有可用于生产的产品乃是第一优先级的事,所以在这一阶段中,最终决定采用主动隔离的方案,后续的迭代中再去调研 ShadowDOM 的方式。