React 组件的 Material UI 主题不在 Shadow DOM 的本地范围内
Posted
技术标签:
【中文标题】React 组件的 Material UI 主题不在 Shadow DOM 的本地范围内【英文标题】:React component's Material UI theme not scoped locally to Shadow DOM 【发布时间】:2019-01-20 18:56:21 【问题描述】:简介
我正在构建一个使用内容脚本呈现 React 组件的 Chrome 扩展程序。 React 组件是一个工具栏,其中包含供用户在浏览页面时使用的子工具。我使用 Material UI 作为我的组件库和工具栏及其所有其他子组件、弹出窗口等的样式解决方案。
什么有效
使用 div 将根 React 组件注入页面可以正常工作,“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(主要反应组件)
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”生成的主题不适用于工具栏,我只剩下默认的内联样式应用于“扩展”div(上面定义,content.js )。
主题是使用来自material-ui
包的createMuiTheme(options)
生成的:
此功能成功,主题应用使用:
<MuiThemeProvider theme=theme>
我可以确认createMuiTheme(options)
& <MuiThemeProvider/>
正在工作,因为主题生成的样式表被添加为页面<head>
标记中的最后一个标记,如图所示:
<head>
tag contains MUI generated styles
我需要做什么
因为我的 React 工具栏元素在 #shadow-root 内,它无法从 MUI 生成的类中接收样式,因为它们包含在主 DOM 树中的主 <head>
标记中。我相信当 <MuiThemeProvider/>
被渲染时,它会将其提供的样式表附加到 main DOM 树的顶部,NOT #shadow-root(它们需要在哪里,以便样式在本地应用)。见下图:
MUI Stylesheets in </head>
, but they need moved to #shadow-root
所以我正在寻找一种解决方案,将 Material UI 生成的样式表放在 #shadow-root 下,以便它们在我的 React 工具栏组件上应用正确的样式。
1.) 有没有办法让影子 DOM 具有 <MuiThemeProvider/>
范围,或者在类名前加上 :host 之类的前缀?
2.) 有没有办法锁定使用react-shadow
创建的#shadow-root,这样当<MuiThemeProvider/>
附加样式表时,它们会被强制附加到影子根目录?
3.) 我对 React、javascript 和创建 Chrome 扩展程序仍然非常缺乏经验。也许我已经错过了一个非常简单的解决方案?
感谢任何帮助。
【问题讨论】:
见How to really isolate stylesheets in the Google Chrome extension? @wOxxOm,感谢您的回复。大部分主题都提到了使用 iFrame 进行隔离,但我已经尝试过了。我有需要在框架边界之外显示的 UI 元素。不过,还讨论了其他主题,这可能会有所帮助。我一直在看material-ui
's CSS in JS article。可以在#shadow-root 中为 JSS Provider 设置自定义 JSS 插入点。 --- Material UI - CSS in JS
@BradenPreston 你找到解决方案了吗?我正在努力实现同样的目标。
@BradenPreston 兄弟,你发现如何解决这个问题了吗?我遇到了完全相同的问题...
【参考方案1】:
我在打包我的 Web 应用程序以与另一个框架集成时遇到了同样的问题,这需要将我的 React 应用程序导出为 Web 组件。在与react-jss
之类的库苦苦挣扎后没有运气,今天早上我终于通过@shawn-mclean 遇到了this Stack Overflow answer。我在用头撞桌子的时候看到了你的问题,想把对我有用的东西传授给我。
总而言之,我首先必须创建 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);
【讨论】:
对于未来的读者,这无需创建自定义 WebComponent 即可工作 这适用于在 shadow DOM 子树中呈现的 MUI 组件,但那些使用门户(如 Dialog)的组件将无法设置样式。参考:github.com/mui-org/material-ui/issues/17473以上是关于React 组件的 Material UI 主题不在 Shadow DOM 的本地范围内的主要内容,如果未能解决你的问题,请参考以下文章
React-Router 和 Material-UI:根据路由应用自定义主题