最近用shadowDOM开发了两个jssdk,样式和逻辑天然地完全隔离,非常好用;
但同时也带来了一些问题,这里一并整理下来。
一、概述
JSSDK
js 编写的软件开发工具包(Software Development Kit),sdk 对应到前端可以是库或组件,为其他软件提供某些功能。
Shadow DOM
一种规范、接口、能力。(MDN说明请点击查看)
可以将一个隐藏的、独立的 DOM 附加到一个元素上,将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁;
必须要附加到普通 DOM 元素上;
Chrome中,设置 –> Elements –> show user agent shadow DOM,可查看 HTML 的复杂组件(文本框、下拉选择、上传文件框等)在浏览器中的部分实现;
ShadowDOM 兼容性
- toC 业务不建议用;
- toB 业务和内部系统可以用;
- 移动端看情况使用;
二、JSSDK结构
打包后的结构如下图,对外只提供一个 jssdk.js,其他资源动态加载;
jssdk.js 中包含三部分逻辑:
- 创建一个元素并给它附加 Shadow DOM;
- 往 ShadowDOM 中插入 js 和 css;
- 定义对外提供的全局方法
集成方调用时,方法内容可能为空,此时只记录一个标识;
等 sdk 内容加载完成,覆盖全局方法,并判断是否有调用过某些全局方法并按需执行;
三、遇到的问题
问题1:全局Vue冲突
产生原因:
页面通过 script 标签引入 Vue 库,或项目中把 Vue 添加到了 window 上,导致 window 上有 Vue 这个函数/类,此时集成平台与组件两者共用一个 Vue 函数/类,往原型上加东西就有可能冲突;
解决方案:
jssdk 中不要直接往 Vue 或 Vue 原型上加内容,禁止使用全局混入和全局指令等;
避免使用 Vue.prototype.xx = xx、Vue.xx=xx、Vue.directive( )、Vue.mixin( ) 这样的写法;
将组件库、全局方法等通过 mixins 引用或加到实例上。
问题2:部分第三方库不能用 – 基础组件库(ElementUI)
产生原因:
html 标签相互隔离,ShadowDOM 外侧和内部的根元素分别为 document 和 shadowRoot;第三方库中如果包含 document 上元素的查询、添加等操作,就可能会报错或出现异常的效果;
解决方案:
把组件库源码拿到项目中,document 上的操作改到 shadowRoot 上,比如
document.body.appendChild 换成 shadowRoot.appendChild 之类;
如果不拿到项目中,也会遇到问题1。
问题3:部分第三方库不能用 – 单一功能的组件库
产生原因:
html 标签相互隔离,ShadowDOM 外侧和内部的根元素分别为 document 和 shadowRoot;第三方库中如果包含 document 上元素的查询、添加等操作,就可能会报错或出现异常的效果;
解决方案:
添加自定义loader或在ES6转ES5的时候处理,动态替换字符串或元素,具体方案需要根据使用的库具体分析。
比如项目中用了一个富文本编辑器,vue-quill-editor.js,在用的时候就需要改为 shadowRoot
vue-quill-editor.js 引用了 quill.js 包,代码里 document.getSelection、document.querySelectorAll、document.querySelector 中的 document 需要替换为 shadowRoot;
* 直接替换成 shadowRoot 又会带来一个新的问题:Firefox里 shadowRoot 上不支持 getSelection 方法;
解决方案:jssdk.js 中添加一个兼容方法,
本来想的是不替换 shadowRoot,直接重写 document.getSelection 方法,但是无法覆盖;
window._SHADOW.getSelection = window._SHADOW.getSelection || function () {
return document.getSelection();
};
问题4:webpack打包,全局函数冲突
产生原因:
打包时使用的是默认全局函数名
解决方案:
在配置文件中进行修改,config.output.jsonpFunction
configureWebpack: config => {
config.output.jsonpFunction = `jsonp_global_fn`;
}
问题5:ShadowDOM 中不支持字体引用
产生原因:
字体文件在 shadowDOM 中引用,没有效果,也不报错,iconfont 不显示;
解决方案:
拿到外侧html中;
四、总结
好用、慎用
优点
- 样式、标签、行为完全隔离;
- 可用来开发 Web Components,更简洁;
缺点
- 第三方组件需改造;
- 兼容性不够完美;