如何在不丢失样式的情况下将material-ui react组件包装到Web组件?

时间:2019-09-04 14:24:33

标签: javascript reactjs components web-component

我正在开发一个包装程序,以将React组件转换为Web组件。在阴影DOM中渲染组件时,我需要应用什么样式?

我试图在Web组件中包装material-ui react组件(按钮),但是,当我将组件附加到影子DOM时,不会应用样式。

//包装

import ReactDOM,{ render } from 'react-dom';

import Button from '@material-ui/core/Button';

class ButtonWrapper extends HTMLElement {
    constructor() {
        super();
    }
    connectedCallback() {
        const mountPoint = document.createElement('span');
        this.attachShadow({ mode: 'open' }).appendChild(mountPoint);
       ReactDOM.render(
            <Button variant="contained" color="primary">
                Material Button
            </Button>
            , mountPoint);  
    }
}

customElements.define('button-material-wrapper', ButtonWrapper);

// HTML

<button-material-wrapper></button-material-wrapper>

// Webpack.config

...
module: {
            rules: [
                {
                    test: /\.js$/,
                    use: {
                        loader: 'babel-loader',
                        options: { presets: ['@babel/preset-env', '@babel/preset-react'] }
                    }
                },
                {
                    test:/\.(s*)css$/,
                    use: ['style-loader', 'css-loader', 'sass-loader']
                }
            ]
        }
....

预期输出: material-ui按钮已正确呈现

实际输出: material-ui按钮被核心地渲染为阴影根的子节点,但未应用材质样式。

1 个答案:

答案 0 :(得分:0)

在webpack中,样式加载器可帮助您将样式添加到全局文档样式中。但是,Web组件无法访问阴影DOM之外的样式(包括全局材质样式),从而导致您的实际输出。 我发现的一种解决方案是使用 css-to-string-loader 手动导入材料组件的scss的字符串,然后手动将其添加到当前阴影DOM的样式中。

// Webpack.config

{
    test: /.*\.scss$/,
    use: ['css-to-string-loader', 'css-loader', 'sass-loader'],
},

//在Web组件中(connectedCallback)

const style = document.createElement('style');
const style.innerHTML = require('@material/xxxx.scss');
const head = document.createElement('head');
head.appendChild(style);
this.attachShadow({ mode: 'open' }).appnedChild(head)

在我看来,它一点也不优雅,期待更好的解决方案。