如何使用 cssinjs/jss 在 shadow root 中挂载样式
How to mount styles inside shadow root using cssinjs/jss
我正在尝试在阴影 dom 中使用 https://material-ui.com/ 组件,并且需要一种方法将这些样式注入阴影 dom 中。默认情况下 material-ui
,它在后台使用 jss
在页面头部注入样式。
这可能吗?谁能举个例子?
这就是我的 Web 组件的样子,它是一个呈现包含 material-ui 样式的 React 应用程序的 Web 组件。
import * as React from 'react';
import { render } from 'react-dom';
import { StylesProvider, jssPreset } from '@material-ui/styles';
import { create } from 'jss';
import { App } from '@myApp/core';
class MyWebComponent extends HTMLElement {
connectedCallback() {
const shadowRoot = this.attachShadow({ mode: 'open' });
const mountPoint = document.createElement('span');
const reactRoot = shadowRoot.appendChild(mountPoint);
const jss = create({
...jssPreset(),
insertionPoint: reactRoot
});
render(
<StylesProvider jss={jss}>
<App />
</StylesProvider>,
mountPoint
);
}
}
customElements.define('my-web-commponent', MyWebComponent);
将 jss 上的 insertionPoint
设置为影子根内的实际反应根将告诉 jss 将这些样式插入影子根内。
使用https://github.com/Wildhoney/ReactShadow创建影子dom(你也可以像前面的回答那样手动完成),我创建了一个封装逻辑的小WrapperComponent。
import root from 'react-shadow';
import {jssPreset, StylesProvider} from "@material-ui/styles";
import {create} from 'jss';
import React, {useState} from "react"
const WrappedJssComponent = ({children}) => {
const [jss, setJss] = useState(null);
function setRefAndCreateJss(headRef) {
if (headRef && !jss) {
const createdJssWithRef = create({...jssPreset(), insertionPoint: headRef})
setJss(createdJssWithRef)
}
}
return (
<root.div>
<head>
<style ref={setRefAndCreateJss}></style>
</head>
{jss &&
<StylesProvider jss={jss}>
{children}
</StylesProvider>
}
</root.div>
)
}
export default WrappedJssComponent
然后你只需要包装你的应用程序,或者你想要隐藏在里面的应用程序部分 <WrappedJssComponenent><YourComponent></YourComponent></WrappedJssComponenent>
。
小心,某些 material-UI 组件将无法正常工作(我遇到了一些问题
- ClickAwayListener,可能是因为它使用了父 dom,老实说,没有调查更多。
- Popper,以及将尝试使用 document.body 作为容器的所有内容将无法访问影子节点中定义的 jss。你应该给阴影 dom 内的一个元素作为容器。
在 MUI 中使用 @material-ui/core/CssBaseline
时,也会使用 emotion
样式。为了同时支持旧版 jss
和 emotion
,您可以使用 CacheProvider
扩展上面接受的答案,如下所示:
import ReactDOM from 'react-dom/client'
import App from './App'
import createCache from '@emotion/cache'
import { CacheProvider } from '@emotion/react';
import { StylesProvider, jssPreset } from '@material-ui/styles';
import { create } from 'jss';
class ReportComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
const mountPoint = document.createElement('div');
const emotionPoint = this.shadowRoot!.appendChild(document.createElement('div'));
const emotionCache = createCache({
key: 'report-component',
container: emotionPoint
});
const reactRoot = this.shadowRoot!.appendChild(mountPoint);
const root = ReactDOM.createRoot(reactRoot);
const jss = create({
...jssPreset(),
insertionPoint: reactRoot
});
root.render(
<StylesProvider jss={jss}>
<CacheProvider value={emotionCache}>
<App />
</CacheProvider>
</StylesProvider>
);
}
}
customElements.define('report-component', ReportComponent);
我正在尝试在阴影 dom 中使用 https://material-ui.com/ 组件,并且需要一种方法将这些样式注入阴影 dom 中。默认情况下 material-ui
,它在后台使用 jss
在页面头部注入样式。
这可能吗?谁能举个例子?
这就是我的 Web 组件的样子,它是一个呈现包含 material-ui 样式的 React 应用程序的 Web 组件。
import * as React from 'react';
import { render } from 'react-dom';
import { StylesProvider, jssPreset } from '@material-ui/styles';
import { create } from 'jss';
import { App } from '@myApp/core';
class MyWebComponent extends HTMLElement {
connectedCallback() {
const shadowRoot = this.attachShadow({ mode: 'open' });
const mountPoint = document.createElement('span');
const reactRoot = shadowRoot.appendChild(mountPoint);
const jss = create({
...jssPreset(),
insertionPoint: reactRoot
});
render(
<StylesProvider jss={jss}>
<App />
</StylesProvider>,
mountPoint
);
}
}
customElements.define('my-web-commponent', MyWebComponent);
将 jss 上的 insertionPoint
设置为影子根内的实际反应根将告诉 jss 将这些样式插入影子根内。
使用https://github.com/Wildhoney/ReactShadow创建影子dom(你也可以像前面的回答那样手动完成),我创建了一个封装逻辑的小WrapperComponent。
import root from 'react-shadow';
import {jssPreset, StylesProvider} from "@material-ui/styles";
import {create} from 'jss';
import React, {useState} from "react"
const WrappedJssComponent = ({children}) => {
const [jss, setJss] = useState(null);
function setRefAndCreateJss(headRef) {
if (headRef && !jss) {
const createdJssWithRef = create({...jssPreset(), insertionPoint: headRef})
setJss(createdJssWithRef)
}
}
return (
<root.div>
<head>
<style ref={setRefAndCreateJss}></style>
</head>
{jss &&
<StylesProvider jss={jss}>
{children}
</StylesProvider>
}
</root.div>
)
}
export default WrappedJssComponent
然后你只需要包装你的应用程序,或者你想要隐藏在里面的应用程序部分 <WrappedJssComponenent><YourComponent></YourComponent></WrappedJssComponenent>
。
小心,某些 material-UI 组件将无法正常工作(我遇到了一些问题
- ClickAwayListener,可能是因为它使用了父 dom,老实说,没有调查更多。
- Popper,以及将尝试使用 document.body 作为容器的所有内容将无法访问影子节点中定义的 jss。你应该给阴影 dom 内的一个元素作为容器。
在 MUI 中使用 @material-ui/core/CssBaseline
时,也会使用 emotion
样式。为了同时支持旧版 jss
和 emotion
,您可以使用 CacheProvider
扩展上面接受的答案,如下所示:
import ReactDOM from 'react-dom/client'
import App from './App'
import createCache from '@emotion/cache'
import { CacheProvider } from '@emotion/react';
import { StylesProvider, jssPreset } from '@material-ui/styles';
import { create } from 'jss';
class ReportComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
const mountPoint = document.createElement('div');
const emotionPoint = this.shadowRoot!.appendChild(document.createElement('div'));
const emotionCache = createCache({
key: 'report-component',
container: emotionPoint
});
const reactRoot = this.shadowRoot!.appendChild(mountPoint);
const root = ReactDOM.createRoot(reactRoot);
const jss = create({
...jssPreset(),
insertionPoint: reactRoot
});
root.render(
<StylesProvider jss={jss}>
<CacheProvider value={emotionCache}>
<App />
</CacheProvider>
</StylesProvider>
);
}
}
customElements.define('report-component', ReportComponent);