ReactJS / Material-ui-SSR中的样式化系统和样式函数

我对使用Material UI以及更具体地说是系统功能(@ material-ui / system)进行服务器端渲染有疑问。

在我的ReactJS应用程序中,我有以下代码段(在此示例中,我使用Material-ui的Flexbox系统道具):

<Box display="flex">Hello</Box>

当服务器向我发送HTML页面时,源代码中显示了“ display”属性,但是当客户端进行水合作用时,我在DOM上找不到此组件的属性“ display:flex”

但是,当我使用'style'属性时,我会在DOM中找到此属性。

<Box style={{display:'flex'}}>Hello</Box>

我想弄清楚,当我以这种方式导入Box组件时,它不起作用:

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

另一方面,当我通过Styled-Component以这种方式导入它时,它会起作用:

import styled from 'styled-components'; 

const Box = styled.div`${display}`;

有关更多信息,这里是我的服务器和客户端的配置。

server.js

import express from 'express';
import fs from 'fs';
import Render from './Render';
import { resolveAppPath } from '../utils/resolve-app-path';
import { conf } from '../../i18n';
const middleware = require('i18next-express-middleware');
const i18next = require('i18next');

const dev = process.env.NODE_ENV === 'development';
import devMiddleware from './dev-middleware';

const app = express();
const port = process.env.PORT || 80;

if (dev) {
    devMiddleware(app);
}
app.use('/assets',express.static(resolveAppPath('dist/assets')));
i18next.use(middleware.LanguageDetector).init(conf);

app.use(middleware.handle(i18next));

app.get('*',(req,res) => {
    const index = resolveAppPath('dist/assets/index.html');

    fs.readFile(index,'utf8',(err,data) => {
        if (err) {
            console.error('error when reading index file : ',err);
            res.status(500).send('error when reading index file');
        }

        res.status(200).send(Render(req,data));
    });
});

app.listen(port,() => {
    console.log('Server started on port : ' + port);
});

Render.js

import ReactDOMServer from 'react-dom/server';
import App from '../shared/components/App';
import React from 'react';
import { ServerStyleSheets } from '@material-ui/core/styles';
import { ServerStyleSheet } from 'styled-components';
import { I18nextProvider } from 'react-i18next';
import { StaticRouter } from 'react-router-dom';

const Render = (req,data) => {
    const context = {};
    const muiSheet = new ServerStyleSheets();
    const scSheet = new ServerStyleSheet();

    try {
        const html = ReactDOMServer.renderToString(
            muiSheet.collect(
                scSheet.collectStyles(
                    <StaticRouter location={req.url} context={context}>
                        <I18nextProvider i18n={req.i18n}>
                            <App />
                        </I18nextProvider>
                    </StaticRouter>,),);

        const muiCss = muiSheet.toString();
        const scCss = scSheet.getStyletags();

        data = data.replace('<div id="root"></div>',`<div id="root">${html}</div>`);

        data = data.replace('<style id="jss-server-side"></style>',`<style id="jss-server-side">${muiCss}</style>`);
        data = data.replace('</head>',`${scCss}</head>`);
    } catch (error) {
        console.error(error);
    } finally {
        scSheet.seal();
    }

    return data;
};

client.js

import React from 'react';
import { hydrate,render } from 'react-dom';
import App from 'Components/App';

import { I18nextProvider } from 'react-i18next';
import i18n from '../../i18n';
import { BrowserRouter } from 'react-router-dom';
import { AppContainer } from 'react-hot-loader';

function Main() {
    React.useEffect(() => {
        const jssStyles = document.querySelector('#jss-server-side');

        if (jssStyles) {
            jssStyles.parentNode.removeChild(jssStyles);
        }
    },[]);

    return <App />;
}

const renderApp = Component => {
    hydrate(
        <AppContainer>
            <BrowserRouter>
                <I18nextProvider i18n={i18n}>
                    <Component />
                </I18nextProvider>
            </BrowserRouter>
        </AppContainer>,document.getElementById('root'),);
};

renderApp(Main);

if (module.hot) {
    module.hot.accept('Components/App',() => {
        renderApp(Main);
    });
}

App.js

import React from 'react';
import CssBaseline from '@material-ui/core/CssBaseline';

import { ThemeProvider } from 'styled-components';
import { ThemeProvider as ThemeProviderMUI,StylesProvider } from '@material-ui/core/styles';
import theme from '../theme';

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

const App = () => {
    return (
        <div>
            <CssBaseline />
            <StylesProvider injectFirst>
                <ThemeProviderMUI theme={theme}>
                    <ThemeProvider theme={theme}>
                        <Box display="flex"></Box>
                    </ThemeProvider>
                </ThemeProviderMUI>
            </StylesProvider>
        </div>
    );
};

export default App;

index.html

<!DOCTYPE html>
<html lang="fr">
    <head>
        <base href="/" />
        <title>Mario Laporte - Développeur web</title>

        <meta name="description" content="Mario Laporte - Développeur web" />
        <meta charset="UTF-8" />
        <meta name="viewport" content="minimum-scale=1,initial-scale=1,width=device-width,shrink-to-fit=no" />
        <meta name="mobile-web-app-capable" content="yes" />
        <!--
  # List of devices and resolutions (AUG-2019):
  #
  #     Device              Portrait size      Landscape size     Screen size        Pixel ratio
  #     iPhone SE            640px × 1136px    1136px ×  640px     320px ×  568px    2
  #     iPhone 8             750px × 1334px    1334px ×  750px     375px ×  667px    2
  #     iPhone 7             750px × 1334px    1334px ×  750px     375px ×  667px    2
  #     iPhone 6s            750px × 1334px    1334px ×  750px     375px ×  667px    2
  #     iPhone XR            828px × 1792px    1792px ×  828px     414px ×  896px    2
  #     iPhone XS           1125px × 2436px    2436px × 1125px     375px ×  812px    3
  #     iPhone X            1125px × 2436px    2436px × 1125px     375px ×  812px    3
  #     iPhone 8 Plus       1242px × 2208px    2208px × 1242px     414px ×  736px    3
  #     iPhone 7 Plus       1242px × 2208px    2208px × 1242px     414px ×  736px    3
  #     iPhone 6s Plus      1242px × 2208px    2208px × 1242px     414px ×  736px    3
  #     iPhone XS Max       1242px × 2688px    2688px × 1242px     414px ×  896px    3
  #     9.7" iPad           1536px × 2048px    2048px × 1536px     768px × 1024px    2
  #     7.9" iPad mini 4    1536px × 2048px    2048px × 1536px     768px × 1024px    2
  #     10.5" iPad Pro      1668px × 2224px    2224px × 1668px     834px × 1112px    2
  #     11" iPad Pro        1668px × 2388px    2388px × 1668px     834px × 1194px    2
  #     12.9" iPad Pro      2048px × 2732px    2732px × 2048px    1024px × 1366px    2
  -->
        <link rel="apple-touch-startup-image" href="icons/ios/startup/portrait/0640x1136_icon.png" media="(min-device-width: 320px) and (max-device-width: 568px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/portrait/0750x1334_icon.png" media="(min-device-width: 375px) and (max-device-width: 667px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/portrait/0828x1792_icon.png" media="(min-device-width: 414px) and (max-device-width: 896px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/portrait/1125x2436_icon.png" media="(min-device-width: 375px) and (max-device-width: 812px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: portrait)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/portrait/1242x2208_icon.png" media="(min-device-width: 414px) and (max-device-width: 736px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: portrait)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/portrait/1242x2688_icon.png" media="(min-device-width: 414px) and (max-device-width: 896px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: portrait)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/portrait/1536x2048_icon.png" media="(min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/portrait/1668x2224_icon.png" media="(min-device-width: 834px) and (max-device-width: 1112px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/portrait/1668x2388_icon.png" media="(min-device-width: 834px) and (max-device-width: 1194px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/portrait/2048x2732_icon.png" media="(min-device-width: 1024px) and (max-device-width: 1366px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)" />

        <link rel="apple-touch-startup-image" href="icons/ios/startup/landscape/1136x0640_icon.png" media="(min-device-width: 320px) and (max-device-width: 568px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: landscape)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/landscape/1334x0750_icon.png" media="(min-device-width: 375px) and (max-device-width: 667px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: landscape)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/landscape/1792x0828_icon.png" media="(min-device-width: 414px) and (max-device-width: 896px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: landscape)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/landscape/2436x1125_icon.png" media="(min-device-width: 375px) and (max-device-width: 812px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: landscape)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/landscape/2208x1242_icon.png" media="(min-device-width: 414px) and (max-device-width: 736px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: landscape)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/landscape/2688x1242_icon.png" media="(min-device-width: 414px) and (max-device-width: 896px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: landscape)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/landscape/2048x1536_icon.png" media="(min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: landscape)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/landscape/2224x1668_icon.png" media="(min-device-width: 834px) and (max-device-width: 1112px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: landscape)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/landscape/2388x1668_icon.png" media="(min-device-width: 834px) and (max-device-width: 1194px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: landscape)" />
        <link rel="apple-touch-startup-image" href="icons/ios/startup/landscape/2732x2048_icon.png" media="(min-device-width: 1024px) and (max-device-width: 1366px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: landscape)" />

        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
        <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />

        <style id="jss-server-side"></style>
    </head>

    <body>
        <div id="root"></div>
        <noscript>
            Vous devez activer JavaScript pour exécuter cette application.
        </noscript>
    </body>
</html>

您知道这种行为是否正常吗? Material UI的系统功能与服务器端渲染不兼容?

我不认为问题出在我的配置上,因为如果不使用系统功能,我也没问题。

此外,渲染是在开发环境中使用dom属性完成的,而不是在生产环境中完成的。真奇怪 在这个问题上我找不到任何答案,您可能有个主意吗?

我还最小化了Webpack的配置,这是我使用的方法:

webpack.common.config.js

const ImageminPlugin = require('imagemin-webpack-plugin').default;
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const { resolveAppPath } = require('../../utils/resolve-app-path');

module.exports = {
    entry: ['babel-polyfill','./src/client'],output: { path: resolveAppPath('dist/assets'),filename: '[name].js',publicPath: '/assets' },resolve: {
        extensions: ['.ts','.tsx','.js','.jsx','.json'],alias: {
            Components: resolveAppPath('src/shared/components'),Src: resolveAppPath('src'),Hooks: resolveAppPath('src/hooks'),Node: resolveAppPath('node_modules'),Img: resolveAppPath('public/images'),'react-dom': '@hot-loader/react-dom',},module: {
        rules: [
            { test: /\.(ts|js)x?$/,exclude: /node_modules/,loader: ['babel-loader','eslint-loader'] },{ test: /\.css$/,use: ['style-loader','css-loader'] },{ test: /\.scss$/,'css-loader','sass-loader'] },{ test: /\.(png|jpg|gif)$/,use: ['file-loader'] },],plugins: [
        new HtmlWebPackPlugin({
            template: resolveAppPath('public/index.html'),favicon: resolveAppPath('public/favicon.ico'),}),new CopyWebpackPlugin([
            {
                from: resolveAppPath('public/images/icons/ios/startup'),to: resolveAppPath('dist/assets/icons/ios/startup'),]),new ImageminPlugin({
            pngquant: {
                speed: 11,quality: 100,cacheFolder: resolveAppPath('cache'),};

webpack.dev.config.js

const webpack = require('webpack');
const { resolveAppPath } = require('../../utils/resolve-app-path');

module.exports = {
    entry: ['react-hot-loader/patch','webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000'],mode: 'development',target: 'web',devtool: 'eval-source-map',plugins: [
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('development'),new webpack.HotModuleReplacementPlugin(),new webpack.NoEmitOnErrorsPlugin(),};

webpack.prod.config.js

const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    mode: 'production',plugins: [
        new CleanWebpackPlugin(),new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }),};

非常感谢您抽出宝贵的时间回答我。

zsg12345 回答:ReactJS / Material-ui-SSR中的样式化系统和样式函数

暂时没有好的解决方案,如果你有好的解决方案,请发邮件至:iooj@foxmail.com
本文链接:https://www.f2er.com/3063576.html

大家都在问