React组件的Material UI主题不在本地范围内适用于Shadow DOM

Bra*_*ton 5 javascript google-chrome-extension shadow-dom reactjs material-ui

介绍

我正在构建一个Chrome扩展程序,该扩展程序使用Content脚本呈现一个React组件。React组件是一个工具栏,其中包含子工具,供用户在浏览页面时使用。我将Material UI用作工具栏及其所有其他子组件,弹出窗口等的组件库和样式解决方案。

什么有效

使用div,“ Extension”作为mountNode,将根React组件注入页面即可。

content.js(Chrome内容脚本)

var app = $('<div id="Extension" style="position: fixed; display: block; bottom: 0px; left: 0px; width: 100vw; height: 48px; background: grey; z-index: 99999"></div>')
app.prependTo('body');
render(<App />, document.getElementById('Extension'))
Run Code Online (Sandbox Code Playgroud)

app.js(主要React组件)

Material UI还可以生成新的样式对象,并将css样式应用于页面上的子组件。所以这一切都很好

(资源)

const styles = {
  root: {
    display: "block",
    color: "red",
  },
};
Run Code Online (Sandbox Code Playgroud)

(生成)

.App-root-1 {
  color: red;
  display: block;
}
Run Code Online (Sandbox Code Playgroud)

问题

由于我在Chrome中使用内容脚本,因此像Facebook这样的带有常规CSS选择器的网站会尝试覆盖中的样式Material UI。CSS属性也有可能从React工具栏泄漏到主页中。

中途解决方案

我当前的解决方案是使用react-shadow围绕React组件的样式范围,并将其与页面的其余部分隔离。

var app = $('<div id="Extension" style="position: fixed; display: block; bottom: 0px; left: 0px; width: 100vw; height: 48px; background: grey; z-index: 99999"></div>')
app.prependTo('body');
render(<App />, document.getElementById('Extension'))
Run Code Online (Sandbox Code Playgroud)

当我这样做时,从“ Material-UI”生成的主题不适用于工具栏,而剩下的是将默认的内联样式应用于“ Extension” div(上面定义为content.js)。

主题是使用createMuiTheme(options)以下material-ui包生成的:

此功能成功,并且使用了以下主题来应用主题:

<MuiThemeProvider theme={theme}>
Run Code Online (Sandbox Code Playgroud)

我可以确认createMuiTheme(options)<MuiThemeProvider/> 是否正常工作,因为主题的生成样式表被添加为页面标签中的最后一个标签<head>,如下图所示:

<head> 标签包含MUI生成的样式

我需要发生什么

因为我的React工具栏元素位于#shadow-root内部,所以它无法从MUI生成的类中接收样式,因为它们包含在<head>主DOM树的主标记中。我相信,当<MuiThemeProvider/>被渲染,它追加其提供样式表的顶部主要 DOM树,不要#影子根(其中,他们需要使样式本地应用)。见下图:

中的MUI样式表</head>,但需要将其移至#shadow-root

因此,我正在寻找一种解决方案,以将Material UI生成的样式表放置在#shadow-root下,以使它们在我的React工具栏组件上应用正确的样式。

1.)有什么方法可以<MuiThemeProvider/>使影子DOM 具有作用域,或在类名前加上诸如:host之类的前缀?

2.)有没有一种方法可以锁定由创建的#shadow-root react-shadow,以便在<MuiThemeProvider/>附加样式表时将其强制附加到shadow根?

3.)我对React,Javascript和创建Chrome扩展程序仍然缺乏经验。也许我已经错过了一个非常简单的解决方案?

任何帮助表示赞赏。

小智 8

我在打包 Web 应用程序以与另一个框架集成时遇到了同样的问题,这需要将我的 React 应用程序导出为 Web 组件。在与诸如此类的库挣扎了一段时间react-jss但运气不佳之后,我终于在今天早上遇到@shawn-mclean 的Stack Overflow 答案。我在用头撞桌子的时候看到了你的问题,想传递对我有用的东西。

总之,我首先必须创建 Web 组件

WCRoot.js

import React from 'react';
import ReactDOM from 'react-dom';
import { StylesProvider, jssPreset } from '@material-ui/styles';
import { create } from 'jss';
import App from './App';

class WCRoot extends HTMLElement {

    connectedCallback() {
        const shadowRoot = this.attachShadow({ mode: 'open' });
        const mountPoint = document.createElement('span');
        // first we need to store a reference to an element INSIDE of the shadow root        
        const reactRoot = shadowRoot.appendChild(mountPoint);

        // now we use that saved reference to create a JSS configuration
        const jss = create({
            ...jssPreset(),
            insertionPoint: reactRoot
        });

        // finally we need to wrap our application in a StylesProvider
        ReactDOM.render(
            <StylesProvider jss={jss}>
                <App />
            </StylesProvider>,
            mountPoint);
    }
}
customElements.define('wc-root', WCRoot);
Run Code Online (Sandbox Code Playgroud)

接下来,我将 my 设置index.js为渲染<wc-component></wc-component>而不是<App />

索引.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
// eslint-disable-next-line
import WCRoot from './WCRoot';

ReactDOM.render(<wc-root></wc-root>, document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)

让我感到惊讶的一件事是我们根本不需要修改MuiThemeProvider...它只是获取StylesProvider. 我相信您应该能够修改content.js为如下所示:

内容.js

var app = $('<div id="Extension" style="position: fixed; display: block; bottom: 0px; left: 0px; width: 100vw; height: 48px; background: grey; z-index: 99999"></div>')
app.prependTo('body');

var shadowRoot = this.attachShadow({ mode: 'open' });
var mountPoint = document.getElementById('Extension');
var reactRoot = shadowRoot.appendChild(mountPoint);

// see code blocks above for the import statements for `create` and `jssPreset`
var jss = create({
    ...jssPreset(),
    insertionPoint: reactRoot
});

render(
    <StylesProvider jss={jss}>
        <App />
    </StylesProvider>,
    mountPoint);
Run Code Online (Sandbox Code Playgroud)

  • 这对于在影子 DOM 子树中渲染的 MUI 组件来说效果很好,但那些使用门户(如 Dialog)的组件将不会被设置样式。参考:https://github.com/mui-org/material-ui/issues/17473 (2认同)