【问题标题】:How to mount styles inside shadow root using cssinjs/jss如何使用 cssinjs/jss 在 shadow root 中挂载样式
【发布时间】:2019-06-23 02:46:55
【问题描述】:

我正在尝试在 shadow dom 中使用 https://material-ui.com/ 组件,并且需要一种将这些样式注入到 shadow dom 中的方法。默认情况下material-ui,它在后台使用jss 在页面头部注入样式。

这可能吗?谁能举个例子?

【问题讨论】:

  • 我不认为将 React 和整个 shebang 注入页面是一个好主意,在我看来当然不是这样。通常的方法是在你的扩展(在 web_accessible_resources 中列出)中只插入一个指向 html 文件的 iframe,这是一个标准页面,可以做任何想做的事情并在其中加载任何材料 UI 组件。我在网上至少看到过一个完整的示例/教程。
  • 你想要一个组件的样式到它的影子 DOM 中还是你可以把所有的样式放到一个影子 DOM 中?
  • @wOxxOm 我完全同意,但是 iframe 方法在我的经验中有点棘手,因为焦点(当您在 iframe 内单击时就像在另一个页面中一样)和调整 iframe 的大小(例如,当我在 iframe 中放置一个下拉列表时,我遇到了问题,我需要告诉外部页面调整 iframe 的大小,以便内容可见)。 Web 组件肯定会更加原生。 “epsilon”我想做的是使用很少的 material-ui 小部件制作一个 Web 组件,并且它们的样式应该注入到 shadow root 中。
  • 即使您能够归档您的目标,您也可能会遇到从影子根内部显示弹出材质组件的问题。因为似乎材料库将弹出窗口 dom 元素直接插入到 html 页面正文中,在您的情况下这将是无样式的。

标签: google-chrome-extension material-ui jss


【解决方案1】:

这就是我的 web 组件的样子,它是一个 web 组件,它呈现一个包含 material-ui 样式的 React 应用程序。

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 设置为 shadow root 中的实际 react root 将告诉 jss 将这些样式插入到该 shadow root 中。

【讨论】:

  • 这适用于许多组件,但是如果你使用 Dialog,它的标签被放置在 body 中,这个技巧不起作用。
【解决方案2】:

使用https://github.com/Wildhoney/ReactShadow 来创建shadow 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

然后你只需要包装你的应用程序,或者你想在&lt;WrappedJssComponenent&gt;&lt;YourComponent&gt;&lt;/YourComponent&gt;&lt;/WrappedJssComponenent&gt;中隐藏的应用程序部分。

小心,一些material-UI组件不能像往常一样工作(我遇到了一些麻烦

  • ClickAwayListener,可能是因为它使用了父 dom,说实话并没有调查更多。
  • Popper,以及所有尝试使用 document.body 作为容器的东西都无法访问影子节点中定义的 jss。您应该在 shadow dom 中提供一个元素作为容器。

【讨论】:

  • 这太棒了!你完全救了我!谢谢!
  • "你应该在 shadow dom 中提供一个元素作为容器。" - 如何做到这一点?
  • @Valu3 您可以参考容器元素将容器道具传递给 Popper(和其他一些元素) - 该元素也可以在阴影根内。
猜你喜欢
  • 2019-04-25
  • 2019-11-28
  • 2021-09-22
  • 2016-06-12
  • 2019-01-14
  • 2020-12-14
  • 2020-06-24
  • 1970-01-01
  • 2021-04-06
相关资源
最近更新 更多