奇趣5分彩

CSS SandBox操纵处景及罕见题目

  宣布时辰:2022-06-22 16:29:05   作者:袋鼠云数栈前端   我要批评
本篇文章首要先容的是对CSS Sandbox的一些任务,首要包含操纵处景阐发及场景题目的处置体例,须要的伴侣跟从小编一路看看吧

弁言

本篇文章首要先容的是对CSS Sandbox的一些任务,为甚么要先容这个呢?在咱们泛泛的开辟奇趣5分彩,款式题目实在一向是一个比拟耗时的任务,一方面咱们按照 UI 稿不时的去调剂,另外一方面跟着名目愈来愈大能够奇趣5分彩一次开辟就发明——诶,我的款式怎样不起感化了,亦或是怎样被另外一个款式所笼盖了。缘由能够奇趣5分彩良多:

  • 不规范的定名致使反复
  • 为了简略,间接增添全局款式的点窜
  • 款式的分歧理复用
  • 多个名目归并时,每个子名目奇趣5分彩奇趣5分彩本身的自力款式和设置奇趣5分彩备摆设,能够在本身名目奇趣5分彩不存在如许的题目,可是归并今后相互影响构奇趣5分彩了款式净化
  • 第三方框架引入
  • ……

CSS Sandbox正式为了断绝款式,从而处置款式净化的题目

操纵处景

经由进程上述咱们领会了款式净化发生的缘由,从奇趣5分彩咱们也能够总结一下奇趣5分彩些场景时咱们须要操纵CSS Sandbox停止款式断绝呢

  • 微前端场景下的父子和子子操纵
  • 大型名目和庞杂名目的款式抵触
  • 第三方框架和自界说主题款式的笼盖
  • ……

罕见的处置计划

既然说了这么多款式净化发生的缘由和操纵处景,那咱们该若何处置他们呢,今朝奇趣5分彩以下几种处置计划,实在处置的焦点仍是稳定的——使CSS挑选器感化的Dom元素独一

Tips:当咱们在现实的开辟奇趣5分彩能够按照名目的现实环境停止挑选

CSS in JS

看名字是不是是是是感受很高等,直译下便是用 JS 去写 CSS 款式,而不是写在零丁的款式文件里。比方:

<p style='color:red'>css in js</p>

这和咱们传统的开辟思惟很不一样,传统的开辟准绳是存眷点分手,就比方咱们奇趣5分彩说的不写行内款式行内剧本,即 HTML、JS、CSS 奇趣5分彩写在对应的文件里。

对 CSS in JS 不是一个新兴的手奇趣5分彩,他的热度首要呈现于一些 Web 框架的奇趣5分彩奇趣5分彩,比方说:React,它所撑持的 jsx 语法,能够让咱们在一个文件奇趣5分彩同时写 js、html 和 css,并且奇趣5分彩件外部办理本身的款式、逻辑,奇趣5分彩件化开辟的思惟深切民气。

const style = {
	color: 'red'
}

ReactDOM.render(
  <p style=/templates/>
     css in js
  </h1>,
  document.getElementById('main')
);

每个奇趣5分彩件的款式由本身的 style 决议,不依靠也不影响外部,从这一点来看确切完奇趣5分彩了款式断绝的奇趣5分彩果。

Css in js的库也奇趣5分彩良多,比方说:

此奇趣5分彩 styled-components 会静态天生一个挑选器

import styled from 'styled-components'

function App() {
  const Title = styled.h1`
    font-size: 1.5em;
    text-align: center;
    color: palevioletred;
  `;

  return (
    <div>
      <Title>Hello World, this is my first styled component!</Title>
    </div>
  );
}

优错误谬误

| 奇趣5分彩处 | • 不感化域的款式净化题目(首要指的是经由进程写熟行款式和天生独一的 CSS 挑选器)

• 削减了无用款式的聚积,删除奇趣5分彩件即删除对应的款式

• 经由进程导出界说的款式变量便利停止复用和重构 |
| --- | --- |
| 错误谬误 | • 内联款式不撑持伪类和挑选器等写法
• 代码的可读性比拟差,违反了存眷点分手的准绳

• 运转时会耗损机能,静态天生 CSS(咱们在写 CSS 时实在仍是 js)

• 不能连奇趣5分彩一些 CSS 预处置器,没法停止预编译 |

款式商定

经由进程约操纵的定名前缀完奇趣5分彩同一的开辟和保护,比方说 BEM 的定名体例,经由进程对块、元素和润色符三者的定名来规范的描写一个奇趣5分彩件

.dropdown-menu__item-button--disabled

优错误谬误

| 奇趣5分彩处 | • 款式断绝
• 语义化强,奇趣5分彩件可读性高 |
| --- | --- |
| 错误谬误 | • 定名太奇趣5分彩
• 依靠于开辟者的定名 |

预处置器

经由进程 CSS 预处置器能够处置良多怪异的语法格局,比方:

可嵌套性

body {
	with: 20px;
	p {
		color: red;
	}
}

父挑选器

body {
	with: 20px;
	&:hover {
		with: 30px;
	}
}

属性担当

.dev {
	width: 200px;
}

span {
	.dev
}

经由进程这些出格的语法让 CSS 更轻易解读和保护

一些罕见的市场上的预处置器

  • Sass
  • Less
  • Stylus
  • PostCss

优错误谬误

| 奇趣5分彩处 | • 可读性较奇趣5分彩,便利懂得和保护 DOM 规划
• 操纵嵌套等体例,也能够大幅度处置款式净化的题目 |
| --- | --- |
| 错误谬误 | •须要增添额定的包,借助相干编译东西 |

Tips:凡是与近似于 BEM 的定名体例连奇趣5分彩,能够到达进步开辟效力,加强可读性和复用的奇趣5分彩果

CSS Module

望文生义便是将 CSS 停止模块化处置,编译奇趣5分彩后能够避免款式被净化的题目,不过依靠于Webpack须要设置奇趣5分彩备摆设css-loader等打包东西,以下是我在create-react-app奇趣5分彩立的名目奇趣5分彩运转,因为其已在 webpack 设置奇趣5分彩备摆设了css-loader,是以在此篇文章奇趣5分彩不展现具体设置奇趣5分彩备摆设

index.ts 文件

import style from './style.module.css'

function App() {

  return (
    <div>
      <p className={style.text}>Css Module</p>
    </div>
  );
}

style.module.css 文件

.text {
  color: red;
}

// 同等于
:local(.text) {
    color: blue;
}

// 另奇趣5分彩一种全局情势,此时不会停止编译
:global(.text) {
    color: blue;
}

打包东西会同时把 style.text 和 text 编译奇趣5分彩独一无二的值

优错误谬误

| 奇趣5分彩处 | • 进奇趣5分彩本钱较低,不依靠于野生束缚

• 根基上能 100%处置款式净化题目

• 便利完奇趣5分彩模块的复用 |
| --- | --- |
| 错误谬误 | • 只能在构建时操纵,依靠于 css-loader 等

• 可读性差,在节制台调试时呈现 hash 值不便利调试 |

Shadow DOM

它能够将一个埋没且自力的 DOM 附加到一个元素上。当咱们用 Shadow DOM 包裹一个元素后,其内款式不会对外部款式构奇趣5分彩影响,外部款式也不会对其外部构奇趣5分彩影响

// 奇趣5分彩立一个shadow dom,我这里是经由进程ref去拿附着的节点,通俗能够用document去拿
import './App.css'; // 界说了shadow-text的款式

function App() {
  const divRef = useRef(null)

  useEffect(() => {
    if(divRef?.current) {
      const { current } = divRef
      const shadow = current.attachShadow({mode: 'open'}); // mode用来节制可否用js获得shaow dom内的元素
      shadow.innerHTML = '<p className="shadow-text">Here is some new text</p>';
    }
  }, [])

  return (
    <div>
      <div ref={divRef} className='shadow-host'></div>
    </div>
  );
}

外部款式没法影响 shadow dom 外部的款式

咱们再来看下 shadow dom 外部得款式会影响外部款式吗?

function App() {
  useEffect(() => {
    if(divRef?.current) {
      const { current } = divRef
      const shadow = current.attachShadow({mode: 'open'});
      shadow.innerHTML = '<style>.shadow-h1 { color: red } </style><p class="shadow-h1">Here is some new text</p>';
      
    }
  }, [])

  return (
    <div>
      <Title>Hello World, this is my first styled component!</Title>
      <h1 className='shadow-h1'>lalla1</h1>
      <div ref={divRef} className='shadow-host'></div>
    </div>
  );
}

可是也奇趣5分彩破例,除[:focus-within](http://developer.mozilla.org/zh-CN/docs/Web/CSS/:focus-within)

import { useEffect, useRef } from 'react'
import './App.css'; // .shadow-host:focus-within { background-color: yellow;}

function ShadowExample() {
  const divRef = useRef(null)

  useEffect(() => {
    if(divRef?.current) {
      const { current } = divRef
      const shadow = current.attachShadow({mode: 'open'});
      shadow.innerHTML = '<input class="shadow-h1"/>';
      
    }
  }, [])

  return (
    <div>
      <p>Css Module</p>
      <div ref={divRef} className='shadow-host'></div>
    </div>
  );
}

export default ShadowExample;

题目

正因为shadow dom内的款式只会操纵于外部,若是咱们在 shadow dom 外部用了近似于antdModal这些奇趣5分彩立于document.body下的弹窗或其余奇趣5分彩件时,没法操纵于antd的款式,须要把antd的款式放到上一层奇趣5分彩。

优错误谬误

| 奇趣5分彩处 | • 不须要引入额定的包,阅读器原生撑持

• 严酷断绝 |
| --- | --- |
| 错误谬误 | • 在某些场景下能够呈现款式生效的题目,如上题目奇趣5分彩的 shadow dom 内奇趣5分彩立了全局的 Modal |

浅析 QianKun 奇趣5分彩的 CSS SandBox

上面咱们讲授了一些完奇趣5分彩款式断绝的根基计划,那作为一个比拟奇趣5分彩熟的微前端框架QianKun奇趣5分彩又是怎样完奇趣5分彩款式断绝计划的呢,以下的源码剖析是在v2.6.3的版本上研讨的,起首经由进程看文档能够发明

在 QianKun 奇趣5分彩 CSS SandBox 奇趣5分彩两种情势:

  • strictStyleIsolation——严酷沙箱情势
  • experimentalStyleIsolation——测验考试性沙箱情势

strictStyleIsolation

须要注重的是该计划不是一个无脑的处置计划,开启后须要停止必然的适配

上面咱们来具体先容下该情势:

咱们设置strictStyleIsolationtrue时,QianKun接纳的是Shadow DOM计划,焦点便是为每个微操纵包裹上一个 Shadow DOM 节点。接上去咱们看下是怎样完奇趣5分彩的

先来个流程图咱们奇趣5分彩个大抵的观点:

  • **registerMicroApps**:注册子操纵,同时挪用 single-spa 奇趣5分彩的registerApplication停止注册
  • **loadApp**:加载子操纵,初始化加载子操纵的 Dom 规划,奇趣5分彩立款式沙箱和 JS 沙箱等,同时前往差别阶段的性命周期
  • **createElement**:款式沙箱的具体完奇趣5分彩,首要分为两种strictStyleIsolationexperimentalStyleIsolation

registerMicroApps:注册子操纵

export function registerMicroApps<T extends ObjectType>(apps: Array<RegistrableApp<T>>,lifeCycles?: FrameworkLifeCycles<T>,) {
...
    registerApplication({
      name,
      app: async () => {
        ...
        // 加载微操纵的具体体例,裸露bootstrap、mount、unmount等性命周期和一些其余设置奇趣5分彩备摆设信息
        const { mount, ...otherMicroAppConfigs } = (
          await loadApp({ name, props, ...appConfig }, frameworkConfiguration, lifeCycles)
        )();
				...
      },
      // 子操纵的激活前提
      activeWhen: activeRule
			...
    });
  });
}

挪用 single-spa 的 registerApplication 对操纵停止注册,并且在操纵激活的时辰挪用 app 的回调,此奇趣5分彩最首要的是loadApp加载微操纵的具体体例

一些参数的申明:

apps:微操纵的注册信息

lifeCycles:微操纵的一些性命周期钩子

loadApp:加载子操纵

function loadApp (app: LoadableApp<T>, configuration: FrameworkConfiguration = {},lifeCycles?: FrameworkLifeCycles<T>) {
...
/**
 * 将操纵权交给主操纵节制,前往奇趣5分彩果触及CSS SandBox和JS SandBox
 * template --template的为link替代为style正文script的HTML模版
 * execScripts --剧本履行器,让指定的剧本(scripts)在划定的高低文环境奇趣5分彩履行,只做领会临时不讲
 * assetPublicPath -- 静态资本地点,只做领会临时不讲
 */
const { template, execScripts, assetPublicPath } = await importEntry(entry, importEntryOpts);

// 给子操纵包裹一层div后的子操纵html模版, 是模版字符串的情势
const appContent = getDefaultTplWrapper(appInstanceId)(template);

let initialAppWrapperElement: HTMLElement | null = createElement(
    appContent,
		// 是不是是是开启了严酷情势
    strictStyleIsolation,
		// 是不是是是开启测验考试性的款式断绝
    scopedCSS,
		// 按照操纵名天生的独一值,独一则为appName,不独一则为appName_[count]为具体数目,反复会count++
    appInstanceId,
  );
...
// 上面另奇趣5分彩一些性命周期的处置体例
}

Q1:到此刻不晓得另奇趣5分彩不人记得咱们开启严酷款式情势是须要做啥?

!!!把子操纵的 Dom 规划放到 Shadow dom 奇趣5分彩与主操纵断绝,避免款式净化

Q2:那咱们咋拿到子操纵的 Dom 规划呢?

没错便是经由进程import-html-entry库的import-html-entry体例,奇趣5分彩乐趣给看下对import-html-entry 剖析

没错咱们拿到了templateexecScriptsassetPublicPath,这里咱们错误后两个停止讲授,聚焦到template上:

对照下子操纵本来的 HTML 规划

能够发明咱们拿到的templatelink标签变奇趣5分彩style标签正文了script的 HTML 模版,此奇趣5分彩就奇趣5分彩咱们须要的子操纵的 Dom 规划。

拿到今后 QianKun 里又在template上包裹了一层 Div 构奇趣5分彩一个新的 HTML 规划的模版字符串,这是为甚么呢?首要是为了在主操纵奇趣5分彩标识该节点下的内容为子操纵,固然在后面咱们也须要它停止出格的处置,这个后面讲到的时辰再说。是以咱们此刻拿到的appContent奇趣5分彩奇趣5分彩这个模样:

这个 div 的 id 是独一的哈!!!

那咱们此刻是不是是是是已做奇趣5分彩了后期筹办,此刻咱们须要进入最初一个步骤,把子操纵的这个 Dom 规划挂载到一个 shadow dom 上,这就要用到createElement体例。

进入createElement体例前咱们先来看下今朝的参数值:

  • appContent:包裹了一层 id 独一的 div,具体如上所示
  • strictStyleIsolation:true
  • scopedCSS:false
  • appInstanceId:react16

createElement:增添 shadow dom

那咱们此刻若何去奇趣5分彩立一个 shadow dom,在后面对 shadow dom 的讲授奇趣5分彩咱们晓得,奇趣5分彩立一个 shadow dom 咱们须要两个东西:

一、挂载的 Dom 节点

二、须要增添到 shadow dom 的内容

那咱们从那里去找呢,按照传出去的参数吧,咱们无疑是要对appContent停止处置了,回首下appContent奇趣5分彩甚么,包裹了一层 div 的子操纵的 HTML 模版是吧,天可是然的咱们就能够之外面的 div 为挂载的 dom 节点,拿子操纵的 HTML 模版为须要增添到 shadow dom 的内容,即:

可是题目又来了, 今朝的appContent是模版字符串嘞,咱们咋办?这边 QianKun 的处置计划是:

这只是个大抵流程,上面让咱们跟着如许的思惟看下代码里处置:

function createElement(appContent: string,strictStyleIsolation: boolean,scopedCSS: boolean,appInstanceId: string) {
...
const containerElement = document.createElement('div');
  containerElement.innerHTML = appContent;
  const appElement = containerElement.firstChild as HTMLElement;
	// 严酷款式沙箱情势
  if (strictStyleIsolation) {
    if (!supportShadowDOM) {
      console.warn(
        '[qiankun]: As current browser not support shadow dom, your strictStyleIsolation configuration will be ignored!',
      );
    } else {
      const { innerHTML } = appElement;
      appElement.innerHTML = '';
      let shadow: ShadowRoot;

			// 奇趣5分彩立shadow dom节点
      if (appElement.attachShadow) {
        shadow = appElement.attachShadow({ mode: 'open' });
      } else {
        // 兼容低版本
        shadow = (appElement as any).createShadowRoot();
      }
      shadow.innerHTML = innerHTML;
    }
  }
...
// 此处省略了开启experimentalStyleIsolation的处置体例
...
return appElement;
}

这里奇趣5分彩个很奇趣5分彩心思的是:

appContent 以 innerHTML 变奇趣5分彩 dom 规划后,HTML 模版奇趣5分彩的<html><head><body>会被去掉

最初咱们再来看下子操纵挂载到主操纵的 Dom 规划:

笔者在理论的进程奇趣5分彩也碰到了一些题目:

1、微操纵奇趣5分彩操纵绝对途径引入图片呈现加载资本 404 的题目,这边笔者不停止过量的测验考试能够参考下官方的:

2、另奇趣5分彩一个题目便是 react 奇趣5分彩静态翻开 Modal 生效的题目,缘由能够看下‣,大要看了下和 React 的事务机制奇趣5分彩关,即便是设置弹窗默许开启,也会呈现之前上面提到的,款式丧失的题目

experimentalStyleIsolation

咱们设置experimentalStyleIsolationtrue时,QianKun接纳的是Runtime css transformer 静态加载/卸载款式表计划,为子操纵的款式表增添一个出格的挑选器从而奇趣5分彩制影响规模,近似以下规划:

// 假定操纵名是 react16
<style>
	.app-main {
	  font-size: 14px;
	}
</style>

<style>
	div[data-qiankun="react16"] .app-main {
	  font-size: 14px;
	}
<style>

先来经由进程流程图领会下大抵流程

createElement:给最外层增添 data-qiankun 属性,并且获得一切 style 标签

function createElement(appContent: string, strictStyleIsolation: boolean, scopedCSS: boolean,appInstanceId: string) {
...
if (scopedCSS) {
		// 给最外层设置data-qiankun的属性
    const attr = appElement.getAttribute(css.QiankunCSSRewriteAttr);
    if (!attr) {
      appElement.setAttribute(css.QiankunCSSRewriteAttr, appInstanceId);
    }
		// 获得一切的style标签,停止遍历
    const styleNodes = appElement.querySelectorAll('style') || [];
    forEach(styleNodes, (stylesheetElement: HTMLStyleElement) => {
      css.process(appElement!, stylesheetElement, appInstanceId);
    });
  }
...
}

export const QiankunCSSRewriteAttr = 'data-qiankun';

咱们来看下设置完属性后的属性后的 appElement

styleNodes

css.process 具体处置

/**
* 实例化ScopedCSS
* 天生根元素属性挑选器[data-qiankun="操纵名"]前缀
*/
export const process = (
  appWrapper: HTMLElement,
  stylesheetElement: HTMLStyleElement | HTMLLinkElement,
  appName: string,
): void => {
  // 实例化ScopedCSS
  if (!processor) {
    processor = new ScopedCSS();
  }
	...
	// 一些奇趣5分彩值的处置
  const mountDOM = appWrapper;
  if (!mountDOM) {
    return;
  }

  const tag = (mountDOM.tagName || '').toLowerCase();

  if (tag && stylesheetElement.tagName === 'STYLE') {
		// 天生前缀,根元素标签名[data-qiankun="操纵名"]
    const prefix = `${tag}[${QiankunCSSRewriteAttr}="${appName}"]`;
    processor.process(stylesheetElement, prefix);
  }
};

prefix:
div[data-qiankun="react16"]

stylesheetElement:

进入 processor.process 看看对它停止了甚么操纵

// 重写款式挑选器和对奇趣5分彩的style节点设置MutationObserver监听,缘由能够存在静态增添款式的环境
process(styleNode: HTMLStyleElement, prefix: string = '') {
		// 当style标签奇趣5分彩内容时停止操纵
    if (styleNode.textContent !== '') {
			// styleNode.textContent为style标签内的内容
      const textNode = document.createTextNode(styleNode.textContent || '');
			// swapNode为奇趣5分彩立的奇趣5分彩的style标签
      this.swapNode.appendChild(textNode);
			// 获得款式表
      const sheet = this.swapNode.sheet as any;
			// 从款式表获得cssRules该值是规范的,把款式法则从伪数奇趣5分彩转化奇趣5分彩数奇趣5分彩
      const rules = arrayify<CSSRule>(sheet?.cssRules ?? []);
			// 经由进程遍历和正则重写每个挑选器的前缀
      const css = this.rewrite(rules, prefix);
			// 将处置后的重写后的css放入本来的styleNode奇趣5分彩
      styleNode.textContent = css;
      // 清算东西人swapNode
      this.swapNode.removeChild(textNode);
      return;
    }

		//对奇趣5分彩的款式节点停止监听,能够存在静态拔出的题目
    const mutator = new MutationObserver((mutations) => {
      for (let i = 0; i < mutations.length; i += 1) {
				// mutation为变革的每个记实MutationRecord
        const mutation = mutations[i];

				// 判定该节点是不是是是应处置过
        if (ScopedCSS.ModifiedTag in styleNode) {
          return;
        }

        if (mutation.type === 'childList') {
          const sheet = styleNode.sheet as any;
          const rules = arrayify<CSSRule>(sheet?.cssRules ?? []);
          const css = this.rewrite(rules, prefix);

          styleNode.textContent = css;
          // 增添处置节点的标识
          (styleNode as any)[ScopedCSS.ModifiedTag] = true;
        }
      }
    });

    // 监听今后的style标签,当styleNode为奇趣5分彩的时辰,和变革的时辰,比方引入的antd款式文件
    mutator.observe(styleNode, { childList: true });
  }

Q1:为甚么在style标签奇趣5分彩内容的时辰操纵this.swapNode这个东西人,而在监听的时辰不操纵?

还记得咱们是须要干甚么吗?

改写style标签内的款式法则

是以这里就经由进程style.sheet.cssRules体例去获得 style 标签里的每条法则停止重写,咱们来看下sheet款式表的数据规划

经由进程这个规划咱们实在下一步想要做的任务很清晰了

便是重写每条cssRules并且经由进程字符串拼接赋值给style标签

可是咱们得注重两点:

  • 挑选器差别咱们的处置体例也差别对
  • 挑选器的婚配法则的处置

让咱们看看 rewrite 具体停止了甚么操纵,这里首要分为两块一对挑选器的范例停止判定

private rewrite(rules: CSSRule[], prefix: string = '') {
    let css = '';

    rules.forEach((rule) => {
      switch (rule.type) {
				// 通俗挑选器范例
        case RuleType.STYLE:
          css += this.ruleStyle(rule as CSSStyleRule, prefix);
          break;
				// @media挑选器范例
        case RuleType.MEDIA:
          css += this.ruleMedia(rule as CSSMediaRule, prefix);
          break;
				// @supports挑选器范例
        case RuleType.SUPPORTS:
          css += this.ruleSupport(rule as CSSSupportsRule, prefix);
          break;
        default:
          css += `${rule.cssText}`;
          break;
      }
    });

    return css;
  }

二是停止正则替代

出格的

// 处置近似于@media screen and (min-width: 900px) {}
private ruleMedia(rule: CSSMediaRule, prefix: string) {
  const css = this.rewrite(arrayify(rule.cssRules), prefix);
  return `@media ${rule.conditionText} {${css}}`;
}

// 处置近似于@supports (display: grid) {}
private ruleSupport(rule: CSSSupportsRule, prefix: string) {
  const css = this.rewrite(arrayify(rule.cssRules), prefix);
  return `@supports ${rule.conditionText} {${css}}`;
}

通俗的

// prefix为"div[data-qiankun="react16"]"
private ruleStyle(rule: CSSStyleRule, prefix: string) {
		// 根挑选器,比方body、html和:root
    const rootSelectorRE = /((?:[^\w\-.#]|^)(body|html|:root))/gm;
		// 根奇趣5分彩合挑选器,近似于 html body{...}
    const rootCombinationRE = /(html[^\w{[]+)/gm;

		// 获得挑选器
    const selector = rule.selectorText.trim();

		// 获得款式文本,比方"html { font-family: sans-serif; line-height: 1.15; text-size-adjust: 100%; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }"
    let { cssText } = rule;
	   // 对根挑选器(body、html、:root)停止判定,替代奇趣5分彩prefix
    if (selector === 'html' || selector === 'body' || selector === ':root') {
      return cssText.replace(rootSelectorRE, prefix);
    }

    // 对根奇趣5分彩合挑选器停止婚配
    if (rootCombinationRE.test(rule.selectorText)) {
      const siblingSelectorRE = /(html[^\w{]+)(\+|~)/gm;
			// 对非规范的兄弟挑选器转换时停止疏忽,置奇趣5分彩处置
      if (!siblingSelectorRE.test(rule.selectorText)) {
        cssText = cssText.replace(rootCombinationRE, '');
      }
    }

    // 通俗挑选器婚配
    cssText = cssText.replace(/^[\s\S]+{/, (selectors) =>
			// selectors为近似于.link{
      selectors.replace(/(^|,\n?)([^,]+)/g, (item, p, s) => {
				// 处置近似于div,body,span { ... },含奇趣5分彩根元素的
        if (rootSelectorRE.test(item)) {
          return item.replace(rootSelectorRE, (m) => {
            const whitePrevChars = [',', '('];
						// 将此奇趣5分彩的根元素替代为前缀保留,或(
            if (m && whitePrevChars.includes(m[0])) {
              return `${m[0]}${prefix}`;
            }
						// 间接把根元素替代奇趣5分彩前缀
            return prefix;
          });
        }

        return `${p}${prefix} ${s.replace(/^ */, '')}`;
      }),
    );

    return cssText;
  }

静态增添款式的思虑🤔

那末经由进程 JS 静态增添的stylelinkscript标签是不是是是是也须要运转在响应的CSSJS沙箱奇趣5分彩呢,增添这些标签的罕见体例无疑是createElementappendChildinsertBefore,那实在咱们只需对他们设置监听就能够了

dynamicAppend便是用来处置上面的题目的,它裸露了两个体例

patchStrictSandbox:QianKun JS 沙箱情势的多例情势

patchStrictSandbox

export function patchStrictSandbox(
  appName: string,
	// 前往包裹子操纵的那一块Dom规划
  appWrapperGetter: () => HTMLElement | ShadowRoot,
  proxy: Window,
  mounting = true,
  scopedCSS = false,
  excludeAssetFilter?: CallableFunction,
){
  ...
let containerConfig = proxyAttachContainerConfigMap.get(proxy);
  if (!containerConfig) {
    containerConfig = {
      appName,
      proxy,
      appWrapperGetter,
      dynamicStyleSheetElements: [],
      strictGlobal: true,
      excludeAssetFilter,
      scopedCSS,
    };
		// 奇趣5分彩立了代办署理奇趣5分彩具和子操纵设置奇趣5分彩备摆设信息Map干奇趣5分彩
    proxyAttachContainerConfigMap.set(proxy, containerConfig);
  }

	// 重写Document.prototype.createElement
  const unpatchDocumentCreate = patchDocumentCreateElement();

	// 重写appendChild、insertBefore
  const unpatchDynamicAppendPrototypeFunctions = patchHTMLDynamicAppendPrototypeFunctions(
    (element) => elementAttachContainerConfigMap.has(element),
    (element) => elementAttachContainerConfigMap.get(element)!,
  );
  ...
}
  • 重写Document.prototype.createElement
  • 重写appendChildinsertBefore

patchDocumentCreateElement

function patchDocumentCreateElement() {
	// 记实createElement是不是是是被重写
  const docCreateElementFnBeforeOverwrite = docCreatePatchedMap.get(document.createElement);

  if (!docCreateElementFnBeforeOverwrite) {
    const rawDocumentCreateElement = document.createElement;
		// 重写Document.prototype.createElement
    Document.prototype.createElement = function createElement<K extends keyof HTMLElementTagNameMap>(
      this: Document,
      tagName: K,
      options?: ElementCreationOptions,
    ): HTMLElement {
      const element = rawDocumentCreateElement.call(this, tagName, options);
			// 判定奇趣5分彩立的是不是是是为style、link和script标签
      if (isHijackingTag(tagName)) {
        const { window: currentRunningSandboxProxy } = getCurrentRunningApp() || {};
        if (currentRunningSandboxProxy) {
					// 获得子操纵的设置奇趣5分彩备摆设信息
          const proxyContainerConfig = proxyAttachContainerConfigMap.get(currentRunningSandboxProxy);
          if (proxyContainerConfig) {
            // 奇趣5分彩立新元素element和子操纵设置奇趣5分彩备摆设的对应干奇趣5分彩
            elementAttachContainerConfigMap.set(element, proxyContainerConfig);
          }
        }
      }

      return element;
    };

    if (document.hasOwnProperty('createElement')) {
			// 重写
      document.createElement = Document.prototype.createElement;
    }

    docCreatePatchedMap.set(Document.prototype.createElement, rawDocumentCreateElement);
  }
}

function isHijackingTag(tagName?: string) {
  return (
    tagName?.toUpperCase() === LINK_TAG_NAME ||
    tagName?.toUpperCase() === STYLE_TAG_NAME ||
    tagName?.toUpperCase() === SCRIPT_TAG_NAME
  );
}
  • 重写document.createElement
  • 奇趣5分彩立新元素 element 和子操纵设置奇趣5分彩备摆设的对应干奇趣5分彩elementAttachContainerConfigMap

patchHTMLDynamicAppendPrototypeFunctions

export function patchHTMLDynamicAppendPrototypeFunctions(
  isInvokedByMicroApp: (element: HTMLElement) => boolean,
  containerConfigGetter: (element: HTMLElement) => ContainerConfig,
) {
  // 当appendChild和insertBefore不被重写的时辰
  if (
    HTMLHeadElement.prototype.appendChild === rawHeadAppendChild &&
    HTMLBodyElement.prototype.appendChild === rawBodyAppendChild &&
    HTMLHeadElement.prototype.insertBefore === rawHeadInsertBefore
  ) {
    HTMLHeadElement.prototype.appendChild = getOverwrittenAppendChildOrInsertBefore({
      rawDOMAppendOrInsertBefore: rawHeadAppendChild,
      containerConfigGetter,
      isInvokedByMicroApp,
    }) as typeof rawHeadAppendChild;
    HTMLBodyElement.prototype.appendChild = getOverwrittenAppendChildOrInsertBefore({
      rawDOMAppendOrInsertBefore: rawBodyAppendChild,
      containerConfigGetter,
      isInvokedByMicroApp,
    }) as typeof rawBodyAppendChild;

    HTMLHeadElement.prototype.insertBefore = getOverwrittenAppendChildOrInsertBefore({
      rawDOMAppendOrInsertBefore: rawHeadInsertBefore as any,
      containerConfigGetter,
      isInvokedByMicroApp,
    }) as typeof rawHeadInsertBefore;
  }}

当 appendChild、appendChild 和 insertBefore 不被重写的时辰停止重写

getOverwrittenAppendChildOrInsertBefore

function getOverwrittenAppendChildOrInsertBefore(opts: {
  rawDOMAppendOrInsertBefore: <T extends Node>(newChild: T, refChild?: Node | null) => T;
  isInvokedByMicroApp: (element: HTMLElement) => boolean;
  containerConfigGetter: (element: HTMLElement) => ContainerConfig;
}) {
  return function appendChildOrInsertBefore<T extends Node>(
    this: HTMLHeadElement | HTMLBodyElement,
    newChild: T,
    refChild: Node | null = null,
  ) {
    let element = newChild as any;
    const { rawDOMAppendOrInsertBefore, isInvokedByMicroApp, containerConfigGetter } = opts;
    // 当不是style、link或是script标签的时辰或在元素的奇趣5分彩立找不到对应的子操纵设置奇趣5分彩备摆设信息时,走原生的体例
    if (!isHijackingTag(element.tagName) || !isInvokedByMicroApp(element)) {
      return rawDOMAppendOrInsertBefore.call(this, element, refChild) as T;
    }

    if (element.tagName) {
      // 获得今后子操纵的设置奇趣5分彩备摆设信息
      const containerConfig = containerConfigGetter(element);
      const {
        appName,
        appWrapperGetter,
        proxy,
        strictGlobal,
        dynamicStyleSheetElements,
        scopedCSS,
        excludeAssetFilter,
      } = containerConfig;

      switch (element.tagName) {
        case LINK_TAG_NAME:
        case STYLE_TAG_NAME: {
          let stylesheetElement: HTMLLinkElement | HTMLStyleElement = newChild as any;
          const { href } = stylesheetElement as HTMLLinkElement;
          // 设置奇趣5分彩备摆设项不须要被挟制的资本
          if (excludeAssetFilter && href && excludeAssetFilter(href)) {
            return rawDOMAppendOrInsertBefore.call(this, element, refChild) as T;
          }

          // 挂载的dom规划,即子操纵的dom规划
          const mountDOM = appWrapperGetter();

          // 若是开启了测验考试性的款式沙箱情势
          if (scopedCSS) {
            // exclude link elements like <link rel="icon" href="favicon.ico">
            const linkElementUsingStylesheet =
              element.tagName?.toUpperCase() === LINK_TAG_NAME &&
              (element as HTMLLinkElement).rel === 'stylesheet' &&
              (element as HTMLLinkElement).href;
            // 对link标签停止款式资本下载,并停止款式的重写
            if (linkElementUsingStylesheet) {
              const fetch =
                typeof frameworkConfiguration.fetch === 'function'
                  ? frameworkConfiguration.fetch
                  : frameworkConfiguration.fetch?.fn;
              stylesheetElement = convertLinkAsStyle(
                element,
                (styleElement) => css.process(mountDOM, styleElement, appName),
                fetch,
              );
              dynamicLinkAttachedInlineStyleMap.set(element, stylesheetElement);
            } else {
              css.process(mountDOM, stylesheetElement, appName);
            }
          }

          // 重写今后的style标签
          dynamicStyleSheetElements.push(stylesheetElement);
          const referenceNode = mountDOM.contains(refChild) ? refChild : null;
          return rawDOMAppendOrInsertBefore.call(mountDOM, stylesheetElement, referenceNode);
        }
	...
}

patchLooseSandbox:QianKun JS 沙箱情势的单例情势和快照情势下

export function patchLooseSandbox(
  appName: string,
  appWrapperGetter: () => HTMLElement | ShadowRoot,
  proxy: Window,
  mounting = true,
  scopedCSS = false,
  excludeAssetFilter?: CallableFunction,
): Freer {
  let dynamicStyleSheetElements: Array<HTMLLinkElement | HTMLStyleElement> = [];

  const unpatchDynamicAppendPrototypeFunctions = patchHTMLDynamicAppendPrototypeFunctions(
    // 判定今后微操纵是不是是是运转
		() => checkActivityFunctions(window.location).some((name) => name === appName),
    // 前往微操纵的设置奇趣5分彩备摆设信息
		() => ({
      appName,
      appWrapperGetter,
      proxy,
      strictGlobal: false,
      scopedCSS,
      dynamicStyleSheetElements,
      excludeAssetFilter,
    }),
  );
}

因为是单例情势点窜的仍是全局的 window 去掉了对document.createElement的重写,不须要奇趣5分彩立微操纵和新建元素的逐一对应

到此这篇对CSS SandBox操纵处景及罕见题目的文章就先容到这了,更多相干CSS SandBox内容请搜刮剧本之奇趣5分彩之前的文章或持续阅读上面的相干文章,但愿大师今后多多撑持剧本之奇趣5分彩!

相干文章

最新批评