簡體   English   中英

React SSR:文檔未定義

[英]React SSR: Document is not defined

我已經為此工作了兩天。 查看了多個堆棧帖子,仍然沒有找到合適的答案。

我正在嘗試在服務器中渲染我的 React 項目,如下所示:

服務器.js

function handleRender(req,res){

  const sheetsRegistry = new SheetsRegistry();

  const sheetsManager = new Map();

  const theme = createMuiTheme({
    palette:{
      primary:green,
      accent: red,
      type: 'light',
    }
  })

  const generateClassName = createGenerateClassName();

  const html = ReactDOMServer.renderToString(
    <JssProvider registry={sheetsRegistry} generateClassName={generateClassName}>
      <MuiThemeProvider theme={theme} sheetsManager={sheetsManager}>
        <TwoFA />
      </MuiThemeProvider>
    </JssProvider>
  )

  const css = sheetsRegistry.toString()

  res.send(renderFullPage(html,css))
}

function renderFullPage(html,css){
  return   `
    <!DOCTYPE html>
    <html>
      <head>
        <title>2FA SDK</title>
      </head>
      <body style="margin:0">
        <div id="app">${html}</div>
        <script id="jss-server-side">${css}</script>
      </body>
    </html>
  `
}

客戶端.js:

import React from 'react';
import ReactDOM from 'react-dom';
import TwoFA from './App';
import {
    MuiThemeProvider,
    createMuiTheme,
    createGenerateClassName,
  } from '@material-ui/core/styles';
import green from '@material-ui/core/colors/green';
import red from '@material-ui/core/colors/red';

class Main extends React.Component{
    componentDidMount() {
        const jssStyles = document.getElementById('jss-server-side');
        if (jssStyles && jssStyles.parentNode) {
          jssStyles.parentNode.removeChild(jssStyles);
        }
    }

    render(){
        return <TwoFA />
    }
}

const theme = createMuiTheme({
    palette: {
      primary: green,
      accent: red,
      type: 'light',
    },
  });

const generateClassName = createGenerateClassName();

if (typeof window !== 'undefined'){
    ReactDOM.hydrate(
        <JssProvider generateClassName={generateClassName}>
          <MuiThemeProvider theme={theme}>
              <TwoFA/>
          </MuiThemeProvider>
        </JssProvider>,
        document.querySelector('#app'),
    );
}

Webpack.config.js

module.exports = [
    {
        /*Config for backend code*/ 
        entry: './src/server/server.js',
        output: {
            filename: 'server.js'
        },
        externals: [nodeExternals()],
        module: {
            rules: [
                {
                    test: /\.(js|jsx)$/,
                    exclude: /node_modules/,
                    use: {
                        loader: "babel-loader"
                    }
                },
                {
                    test: /\.html$/,
                    use: {
                        loader: "html-loader",
                        options: { minimize: true }
                    }
                },
                {
                    test: /\.css$/,
                    use: [MiniCssExtractPlugin.loader,"css-loader"]
                }
            ]
        },
        plugins: [
            new HtmlWebPackPlugin({
                template: "./public/index.html",
                filename:"./index.html"
            }),
            new MiniCssExtractPlugin({
                filename: "[name].css",
                chunkFilename:"[id].css"
            })
        ]
    },
    { 
        entry: './src/client.js',
        output: {
           filename: 'bundle.js',
        },
        module: {
           rules: [
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    loader: 'babel-loader',
                },
                {
                    test: /\.html$/,
                    use: {
                        loader: "html-loader",
                        options: { minimize: true }
                    }
                },
                {
                    test: /\.css$/,
                    use: [MiniCssExtractPlugin.loader,"css-loader"]
                }
            ],
        },
        plugins: [
            new HtmlWebPackPlugin({
                template: "./public/index.html",
                filename:"./index.html"
            }),
            new MiniCssExtractPlugin({
                filename: "[name].css",
                chunkFilename:"[id].css"
            })
        ]
    }
]

我嘗試過的:我在 SO 上搜索,發現許多帖子建議進行條件檢查,如下所示: if (typeof window !== 'undefined') 但是,這並不能解決問題。

我也了解到這個錯誤是因為在SSR期間,服務器端沒有文檔項目。

我在 github 問題頁面上搜索過,有人提到他遇到了 webpack 的問題,但同一個項目在 browserify 上運行良好。

我需要什么幫助:我正在嘗試解決這個問題,因為它會導致應用程序崩潰。

我懷疑 webpack 有問題。 我正在尋找解決此問題的方法

這個問題通常發生是因為在服務器上呈現 react 時。 它在服務器端沒有文檔或窗口對象,這些對象僅在瀏覽器上可用。

嘗試在componentDidMount中或之后調用文檔函數。

componentDidMount(){
  this.setState({documentLoaded:true});
}

someFunction(){
  const { documentLoaded } = this.state;
  if(documentLoaded){
     // LOGIC USING DOCUMENT OBJECT
  }
}

如果您使用的是 react-hooks,則可以創建自定義 useDocument 掛鈎:

import React, { useEffect, useState } from 'react'

export const useDocument = () => {
  const [myDocument, setMyDocument] = useState(null)
   
  useEffect(() => {
    setMyDocument(document)
  }, [])

  return myDocument
}

在您的組件中:

...
const doc = useDocument()
...

<SomeComponent
 ref={doc && doc.body}
 ...
/>
...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM