介绍
我正在构建一个 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/>
呈现后,它将其提供的样式表附加到main 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/