javascript - React 组件的 Material UI 主题不局限于 Shadow DOM

标签 javascript reactjs google-chrome-extension material-ui shadow-dom

介绍

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

什么有效

将根 React 组件注入(inject)页面工作正常,使用 div,“Extension”作为 mountNode。

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'))

app.js(主要 React 组件)

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

(来源)

const styles = {
  root: {
    display: "block",
    color: "red",
  },
};

(生成)

.App-root-1 {
  color: red;
  display: block;
}

问题

因为我在 chrome 中使用内容脚本,像 Facebook 这样带有通用 css 选择器的网站会尝试覆盖 Material UI 中的样式. css 属性也有可能从 React 工具栏泄漏到主页中。

中途解决方案

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

import React from "react";
import PropTypes from 'prop-types';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import { withStyles } from '@material-ui/core/styles';

import ShadowDOM from 'react-shadow';
import ToolBar from './ToolBar'

const theme = createMuiTheme({
  palette: {
    primary: {
      main: '#ef5350',
    },
    type: 'light'
  },
  status: {
    danger: 'orange',
  },
});

const styles = {
  root: {
    display: "block",
    color: "red",
  },
};

class App extends React.Component {

  constructor(props) {
      super(props);
  
      this.state = { open: false }
  }

  render () {
    const { classes } = this.props;

    return (
      <ShadowDOM>
          <div id="Toolbox">
              <MuiThemeProvider theme={theme}>
                  <ToolBar />
              </MuiThemeProvider>
          </div>    
      </ShadowDOM>
    )
  }
};

App.propTypes = {
  classes: PropTypes.object,
};

export default withStyles(styles)(App)

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

主题是使用createMuiTheme(options)生成的来自 material-ui包裹:

此功能成功,应用主题使用:

<MuiThemeProvider theme={theme}>

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

<head> tag contains MUI generated styles

我需要发生什么

因为我的 React 工具栏元素在#shadow-root 中,它无法从 MUI 生成的类中接收样式,因为它们包含在主 <head> 中。标记,在主 DOM 树中。我相信当<MuiThemeProvider/>呈现后,它将其提供的样式表附加到ma​​in DOM 树的顶部,不是 #shadow-root(它们需要放在什么地方,以便样式在本地应用)。见下图:

MUI Stylesheets in </head> , but they need moved to #shadow-root

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

1.) 有没有办法得到<MuiThemeProvider/>影子 DOM 的作用域,或者在类名前加上 :host?

2.) 有没有办法锁定用react-shadow 创建的#shadow-root? ,所以当<MuiThemeProvider/>附加样式表,它们被强制附加到影子根?

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

感谢任何帮助。

最佳答案

我在打包我的 Web 应用程序以便与另一个框架集成时遇到了同样的问题,这需要我的 React 应用程序作为 Web 组件导出。在与 react-jss 这样的图书馆苦苦挣扎了一段时间之后运气不好,我终于遇到了this Stack Overflow answer通过 @shawn-mclean今天早上。我在用头撞 table 时看到了你的问题,想传递对我有用的东西。

总而言之,我首先必须创建 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);

接下来,我设置我的 index.js呈现 <wc-component></wc-component>而不是 <App /> :

index.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'));

让我感到惊讶的一件事是我们不需要修改 MuiThemeProvider根本...它只是获取由 StylesProvider 提供的现有 JSS 上下文.我相信你应该可以修改 content.js看起来像下面这样:

content.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);

关于javascript - React 组件的 Material UI 主题不局限于 Shadow DOM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51832583/

相关文章:

javascript - 函数无法访问扩展程序中的 Chrome 存储值

javascript - JavaScript Web 组件中的 createElement() 按钮未显示

jquery - WordPress/Elementor 实现类似 React 的 Framer's Motion 的页面过渡效果

reactjs - 我应该在 Amazon s3 上部署动态页面吗?

javascript - Chrome 扩展 - getUserMedia 抛出 "NotAllowedError: Failed due to shutdown"

google-chrome-extension - 关于 chrome.tabs.executeScript(id,details, callback)

javascript - 触发.io — 谷歌分析

javascript - 如何从 JSON 中提取特定值到 map 上?

javascript - 内部函数如何知道这个参数?

javascript - 无法读取未定义 ReactJS 的属性 'setState'