代码之家  ›  专栏  ›  技术社区  ›  beuluis

MaterialUI样式在带Vite的Shadow DOM中安装不正确的问题

  •  0
  • beuluis  · 技术社区  · 1 年前

    当使用Vite作为bundler时,我遇到了在shadow DOM中挂载MaterialUI样式的问题。我已经按照MaterialUI文档中概述的步骤进行了操作( https://mui.com/material-ui/guides/shadow-dom/ )通过合并shadow DOM来解决CSS出血问题。在本地,在Vite的开发模式下,一切都按预期进行。

    然而,当我构建web组件并将MaterialUI与其他资源一起外部绑定时,样式似乎安装在shadow DOM之外,导致了意外的行为。我参考了上的文档 https://emotion.sh/docs/@emotion/cache#container ,这建议为shadow DOM上下文中的样式提供一个容器。

    正如文档中所建议的,我还调整了主题,为MaterialUI中的某些组件指定了一个新的安装点。

    奇怪的是,在开发模式下,一切都能在本地按预期工作,但在构建web组件后,样式安装不正确。我怀疑这个问题可能与MaterialUI的外部绑定有关,因为将其从外部依赖项中删除并直接与我的web组件绑定可以解决这个问题。

    如果您对如何在外部捆绑MaterialUI和Vite时确保阴影DOM中的正确样式安装有任何见解或建议,我们将不胜感激。非常感谢。

    我能够制作一个最小的例子: https://github.com/beuluis/MUI-Shadow-DOM-CacheProvider-Broken-Theme

    vite.fig.ts

    import react from '@vitejs/plugin-react';
    import { defineConfig } from 'vite';
    import viteTsconfigPaths from 'vite-tsconfig-paths';
    
    const environmentName = process.env.npm_package_name;
    
    if (!environmentName) {
        throw new Error('Missing package name in environment variables. Vue CLI should set this.');
    }
    
    const name = environmentName.split('/').pop();
    
    export default defineConfig({
        plugins: [react(), viteTsconfigPaths()],
        build: {
            sourcemap: true,
            rollupOptions: {
                external: ['@emotion/react', '@emotion/styled', '@mui/material', 'react', 'react-dom'],
                output: {
                    format: 'iife',
                    assetFileNames: `${name}.[hash].[extname]`,
                    chunkFileNames: `${name}.[hash].chunk.js`,
                    entryFileNames: `${name}.min.js`,
                    globals: {
                        react: 'React',
                        'react-dom': 'ReactDOM',
                        '@mui/material': 'MaterialUI',
                        '@emotion/react': 'emotionReact',
                        '@emotion/styled': 'emotionStyled',
                    },
                },
            },
            outDir: 'dist',
        },
    });
    
    

    应用.tsx

    import { StrictMode, Suspense } from 'react';
    import { Route, Routes } from 'react-router-dom';
    import createCache from '@emotion/cache';
    import { CacheProvider } from '@emotion/react';
    import { createTheme, ThemeProvider } from '@mui/material';
    import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
    
    import { Loading } from '@company/partner-ui'; // company component lib
    import { theme } from '@company/partner-ui/dist/theme'; // company theme
    import { NamespacedRouter } from '@company/react-namespaced-router'; // company router to support specific url handling
    import { useWidget } from '@company/web-component-core'; // company core lib for web-components. takes over mounting and attribute watching etc
    import { ApiContextProvider } from './contexts/api';
    import { useLanguage } from './hooks/useLanguage';
    import { PartnerData } from './pages/PartnerData/PartnerData';
    
    export interface AppProperties {
        readonly apiBaseUrl: string;
        readonly locale: string;
        readonly routerName?: string;
        readonly basename?: string;
    }
    
    export const App = ({
        apiBaseUrl,
        basename,
        locale,
        routerName = 'partner-data-frontend',
    }: AppProperties) => {
        useLanguage(locale);
    
        // This all is in out lib that provides also useWidget(); I wrote a rough example how it will create those containers.
        // const root = this.shadowRoot ?? this;
    
        // const containerElement = create('root');
        // const stylesContainer = create('styles');
        // const customStylesPoint = create('custom-styles');
    
        // root.append(stylesContainer, containerElement);
        // stylesContainer.append(customStylesPoint);
        const { containerElement, stylesContainer } = useWidget();
    
        const cache = createCache({
            key: 'css',
            prepend: true,
            container: stylesContainer,
        });
    
        const shadowDomTheme = createTheme(theme, {
            components: {
                MuiPopover: {
                    defaultProps: {
                        container: containerElement,
                    },
                },
                MuiPopper: {
                    defaultProps: {
                        container: containerElement,
                    },
                },
                MuiModal: {
                    defaultProps: {
                        container: containerElement,
                    },
                },
            },
        });
    
        const queryClient = new QueryClient();
    
        return (
            <StrictMode>
                <CacheProvider value={cache}>
                    <ThemeProvider theme={shadowDomTheme}>
                        <Suspense fallback={<Loading />}>
                            <QueryClientProvider client={queryClient}>
                                <NamespacedRouter name={routerName} basename={basename}>
                                    <ApiContextProvider baseUrl={apiBaseUrl}>
                                        <Routes>
                                            <Route path="/">
                                                <Route index element={<PartnerData />} />
                                            </Route>
                                        </Routes>
                                    </ApiContextProvider>
                                </NamespacedRouter>
                            </QueryClientProvider>
                        </Suspense>
                    </ThemeProvider>
                </CacheProvider>
            </StrictMode>
        );
    };
    
    
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
    
            <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
            <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
            <script src="https://unpkg.com/@mui/material@5/umd/material-ui.production.min.js"></script>
            <script src="https://unpkg.com/@emotion/react@11/dist/emotion-react.umd.min.js"></script>
            <script src="https://unpkg.com/@emotion/styled@11/dist/emotion-styled.umd.min.js"></script>
    
            <title>demo page</title>
            <script type="module" src="src/App.tsx"></script>
        </head>
        <body>
            <!-- This gets mounted by our company web-component-core lib -->
            <partner-data-frontend api-base-url="" locale="en-GB"></partner-data-frontend>
        </body>
    </html>
    

    我试过:

    1. 测试不同的捆绑器。失败,因为此捆绑程序不支持所需的所有内容
    2. 尝试最小化代码。失败
    3. 尝试绑定MUI,但不将其外部化。有效,但不是真正的解决方案
    4. 尝试了不同的安装容器。失败
    5. 尝试过但没有使用备注
    0 回复  |  直到 1 年前