全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2022-10-12_Qiankun实践——实现一个CSS沙箱

您的位置:首页 >> 新闻 >> 行业资讯

Qiankun实践——实现一个CSS沙箱 本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究! 前言哈喽,大家好,我是海怪。 上篇文章讲了如何实现一个 Qiankun 的 JS 沙箱(实际应该是 3 个,哈哈),那这篇文章就带大家来实现一下 CSS 的沙箱。 Qiankun 的 CSS 沙箱原理上并不难,但与 JS 沙箱不同的是,它的源码比较分散,阅读起来要跳转好几个地方,有点麻烦。因此,这篇文章同样也会精简整个实现过程,尽量让读者读起来不费劲。 文章中的源码都放在我的这个仓库 mini-css-sandbox 里,需要的自行提取即可。废话不多说,那现在就让我们开始吧~ 准备工作首先,我们来做一些准备工作,分别添加以下文件: index.html:入口 HTMLshadowDOMIsolation.js:Shadow DOM 沙箱实现scopedCSSIsolation.js:Scoped CSS 沙箱实现因为样式是否成功隔离可以通过肉眼去看,这里就不用 TDD 方式来做测试了,文章也会更精简一些。 在index.html里添加: htmllang="en" head metacharset="UTF-8" title样式隔离沙箱/title style p{ color: } /style /head body h1ShadowDOM隔离/h1 divid="shadow-dom" pShadowDOM隔离/p /div h1ScopedCSS隔离/h1 divid="scoped-css" pScopedCSS隔离/p /div p外部文本/p scriptsrc="scopedCSSIsolation.js"/script scriptsrc="shadowDOMIsolation.js"/script /body /html 这个 HTML 里有一个全局的style,里面有全局样式,会将的颜色变成红色,剩下的都是一些测试要用的 HTML 结构。 Shadow DOM 沙箱我们先来实现 Shadow DOM 沙箱,它对应Qiankun 样式隔离的严格模式。 原理在开始写代码前,我们来简单了解一下 Shadow DOM 原理。 Shadow DOM 可以将一个隐藏的、独立的 DOM 附加到一个元素上,一般来说是微应用的容器div上。 其中: Shadow host:一个常规 DOM 节点,Shadow DOM 会被附加到这个节点上。Shadow tree:Shadow DOM 内部的 DOM 树。Shadow boundary:Shadow DOM 结束的地方,也是常规 DOM 开始的地方。Shadow root: Shadow tree 的根节点。这并不是什么新技术,我们常见的video和audio用的就是 Shadow DOM,浏览器把一些相关逻辑封装和内部结构封装在里面。外部看来就是一个video但里面包含着对应的按钮、轨道、滚动条等结构。 实现假如我们把微应用的内容用 Shadow DOM 封装起来,比如把style挂截到 Shadow Tree 上,那么就可以实现样式的硬隔离了。 假如有下面的微应用,里面有一个style会把字体颜色改成紫色: constshadowDOMSection=document.querySelector('#shadow-dom'); constappElement=shadowDOMIsolation(` divclass="wrapper" stylep{color:purple}/style p内部文本 /div `); shadowDOMSection.appendChild(appElement); 我们现在要做的就是把div.wrapper与外部隔离,不要让里面的style影响到外部的。按照刚刚对 Shadow DOM 的理解,我们来实现一下: functionshadowDOMIsolation(contentHtmlString){ //清理HTML contentHtmlString=contentHtmlString.trim(); //创建一个容器div constcontainerElement=document.createElement('div'); //生成内容HTML结构 containerElement.innerHTML=contentHtmlString;//content最高层级必需只有一个div元素 //获取根div元素 constappElement=containerElement.firstChild; const{innerHTML}=appElement; //清空内容,以便后面绑定shadowDOM appElement.innerHTML=''; letshadow; if(appElement.attachShadow){ //兼容性更广的写法 shadow=appElement.attachShadow({mode:'open' }else{ //旧写法 shadow=appElement.createShadowRoot(); } //生成shadowDOM的内容 shadow.innerHTML=innerHTML; returnappElement; } 可以看到 Shadow DOM 沙箱实现还是比较简单的,主要做了几件事: 把当前元素的内容拿出来生成shadowDOM再刚刚的内容放入这个 shadow DOM清除这个元素,并追加 shadow DOM 即可最终效果如下: 会发现外部文本依然是红色,不会受微应用的样式影响。 Scoped CSS 沙箱接下来讲讲 Scoped CSS 沙箱,它对应的是Qiankun 样式隔离的实验性模式。 原理Scoped CSS 沙箱的原理更简单:将微应用里的style的文本提取出来,将所有的选择器进行转换: 普通选择器-微应用容器选择器普通选择器 例如: span -div[data-app-name=我的微应用]span 这样span的样式只会作用在div[data-app-name=我的微应用]元素,而不会跑到外面了。 实现原理很简单,但实现起来还是有些复杂的。其中比较绕的一个点就是获取 CSS 文本: functionprocessCSS(appElement,stylesheetElement,appName){ //生成 CSS 选择器:div[data-app-name=微应用名字] constprefix=`${appElement.tagName.toLowerCase()}[data-app-name="${appName}"]`; //生成临时style节点 consttempNode=document.createElement('style'); document.body.appendChild(tempNode); tempNode.sheet.disabled=true if(stylesheetElement.textContent!==''){ //将原来的CSS文本复制一份到临时style上 consttextNode=document.createTextNode(stylesheetElement.textContent||''); tempNode.appendChild(textNode); //获取CSS规则 constsheet=tempNode.sheet; construles=[...sheet?.cssRules?? //生成新的CSS文本 stylesheetElement.textContent=this.rewrite(rules,prefix); //清理 tempNode.removeChild(textNode); } } functionscopedCSSIsolation(appName,contentHtmlString){ //清理HTML contentHtmlString=contentHtmlString.trim(); //创建一个容器div constcontainerElement=document.createElement('div'); //生成内容HTML结构 containerElement.innerHTML=contentHtmlString;//content最高层级必需只有一个div元素 //获取根div元素 constappElement=containerElement.firstChild; //打上data-app-name=appName的标记 appElement.setAttribute('data-app-name',appName); //获取所有style/style元素内容,并将它们做替换 conststyleNodes=appElement.querySelectorAll('style')|| [...styleNodes].forEach((stylesheetElement)={ processCSS(appElement,stylesheetElement,appName); }) returnappElement; } 由于我们要改成div[data-app-name=我的微应用] span,所以第一个参数为应用名,以此作区分。接下来是对 CSS 文进行替换了,这部分 Qiankun 用了好几replace,属实有点绕,我这里就精简成对最常见的情况p { color: blue }进行替换: //多种规则 constRuleType={ STYLE:1, MEDIA:4, SUPPORTS:12, } functionruleStyle(rule,prefix){ //匹配p{...,a{...,span {...这类字符串 returnrule.cssText.replace(/^[\s\S]+{/,(selectors)={ //匹配div,body,span {...这类字符串 returnselectors.replace(/(^|,\n?)([^,]+)/g,(selector,_,matchedString)={ //将p{=div[data-app-name=微应用名]p{ return`${prefix}${matchedString.replace(/^*/,'')}`; }) } functionrewrite(rules,prefix){ letcss=''; rules.forEach((rule)={ switch(rule.type){ caseRuleType.STYLE: css+=ruleStyle(rule,prefix); break; //caseRuleType.MEDIA: //css+=this.ruleMedia(rule,prefix); //break; //caseRuleType.SUPPORTS: //css+=this.ruleSupport(rule,prefix); //break; default: css+=`${rule.cssText}`; break; } return } 可以看到除了STYLE规则,还有媒体以及兼容性的规则,这些 Qiankun 都有对于的正则匹配去改写 CSS,这里只关注 STYLE 就可以了。当然,这部分基本就是正则的替换,我认为不需要花太多时间纠结正则表达式是怎么写的,只要理解整体思路就好。 同样写一个用例测试一下: constscopedCSSSection=document.querySelector('#scoped-css'); constwrappedScopedCSSAppElement=scopedCSSIsolation('MyApp',` divclass="wrapper" stylep{color:blue}/style pScopedCSSIsolation /div `); scopedCSSSection.appendChild(wrappedScopedCSSAppElement); 效果如下: 可以看到,外部文本依然为红色,而内部文本为蓝色。打开控制台也可以到对应的 CSS 选择器已做了改写: 上面为微应用样式,下面被划掉的为全局样式变成 Web Component问题可以发现上面的代码有一些重复:每次都要获取 container 元素,写好 HTML,最后再appendChild追加appElement。有重复,我们就应该用一个函数去封装好它,这才是良好的写代码习惯。 通常来说写个函数包装一下就好了,Qiankun 也是如此。不过,这里我想跳脱 Qiankun 微前端的范畴,我希望不要自己手写htmlString,而是可以这样去使用: h1ShadowDOM隔离/h1 isolation-contentdata-app-name="Sub1"data-isolation-mode="shadowDOM" stylep{color:purple}/style pShadowDOMIsolation/p /isolation-content h1ScopedCSS隔离/h1 isolation-contentdata-app-name="Sub2"data-isolation-mode="scopedCSS" stylep{color:blue}/style pScopedCSSIsolation/p /isolation-content p外部文本/p 这其实就是 Web Component 了,其它微应用框架 single-spa 周边库和京东的 MicroApp 也用到同样的技术。 实现Web Componet 不多介绍,具体可以看我的这篇《秒懂 Web Component》。按 Web Component 的理念来实现一下: classIsolationextendsHTMLElement{ constructor(){ super(); constname=this.getAttribute('data-app-name'); constmode=this.getAttribute('data-isolation-mode'); consthtml=`divclass="wrapper"${this.innerHTML.trim()}/div`; //根据隔离模式来生成对应的appElement constappElement=mode==='shadowDOM'?shadowDOMIsolation(html):scopedCSSIsolation(name,html); //清除内容 this.innerHTML=''; //再追加包裹的内容 this.appendChild(appElement); } } customElements.define('isolation-content',Isolation) 还要记得在index.html里引入这个文件: scriptsrc="scopedCSSIsolation.js"/script scriptsrc="shadowDOMIsolation.js"/script scriptsrc="Isolation.js"/script 最终的效果如下: 总结最后我们来总结一下这篇实践: Qiankun 的样式隔离主要分为Shadow DOM 隔离以及Scoped CSS 隔离两种Shadow DOM 隔离主要利用了 Shadow DOM 硬隔离的特点来做样式隔离Scoped CSS 则是对style元素的 CSS 文本进行处理,在原有选择器上添加下个父类选择器,以此做样式隔离 阅读原文

上一篇:2025-07-28_75天剪辑师&导演&调色课程目录 下一篇:2023-05-21_大羊驼、羊驼、小羊驼和ChatGPT比差在哪儿?CMU副教授测评了7个类ChatGPT大模型

TAG标签:

17
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价