使用ShadowDOM开发基于Vue的JSSDK

最近用shadowDOM开发了两个jssdk,样式和逻辑天然地完全隔离,非常好用;

但同时也带来了一些问题,这里一并整理下来。

一、概述

JSSDK

js 编写的软件开发工具包(Software Development Kit),sdk 对应到前端可以是库或组件,为其他软件提供某些功能。

Shadow DOM

一种规范、接口、能力。(MDN说明请点击查看

可以将一个隐藏的、独立的 DOM 附加到一个元素上,将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁;

必须要附加到普通 DOM 元素上;

Chrome中,设置 –> Elements –> show user agent shadow DOM,可查看 HTML 的复杂组件(文本框、下拉选择、上传文件框等)在浏览器中的部分实现;

ShadowDOM 兼容性

  1. toC 业务不建议用;
  2. toB 业务和内部系统可以用;
  3. 移动端看情况使用;

二、JSSDK结构

打包后的结构如下图,对外只提供一个 jssdk.js,其他资源动态加载;

jssdk.js 中包含三部分逻辑:

  1. 创建一个元素并给它附加 Shadow DOM;
  2. 往 ShadowDOM 中插入 js 和 css;
  3. 定义对外提供的全局方法

集成方调用时,方法内容可能为空,此时只记录一个标识;
等 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中;

四、总结

好用、慎用

优点

  1. 样式、标签、行为完全隔离;
  2. 可用来开发 Web Components,更简洁;

缺点

  1. 第三方组件需改造;
  2. 兼容性不够完美;

如果这篇文章对你有用,可以点击下面的按钮告诉我

0

发表回复