簡體   English   中英

React SSR ReferenceError:文檔未定義

[英]React SSR ReferenceError: document is not defined

我無法呈現 React 服務器端。 我總是得到^

參考錯誤:文檔未定義

var root = document.getElementById('root');

我知道節點不理解瀏覽器文檔對象是怎么回事。 但我似乎無法修復它。 我在 webpack、服務器或 app.js 中做錯了什么嗎?

這是我的代碼

服務器.js

import express from 'express';
import path from 'path';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import configureStore from './src/store/configureStore';
import routes from './src';

const app = express();

app.use(express.static(path.join(__dirname, 'build')));

/**
 * React application route, supports server side rendering.
 */
app.get('/*', function(req, res) {
  // Create a context for <StaticRouter>, which will allow us to
  // query for the results of the render.
  const reactRouterContext = {};

  // Compile an initial state
  const preloadedState = {};

  // Create a new Redux store instance
  const store = configureStore(preloadedState);

  // Declare our React application.
  const app = (
    <Provider store={store}>
      <StaticRouter location={req.url} context={reactRouterContext}>
        {routes}
      </StaticRouter>
    </Provider>
  );
  const appString = renderToString(app);
  // Load contents of index.html
  fs.readFile(
    path.join(__dirname, 'build', 'index.html'),
    'utf8',
    (err, data) => {
      if (err) throw err;

      // Inserts the rendered React HTML into our main div
      const document = data.replace(
        /<main id="root"><\/main>/,
        `<main id="root">${app}</main>`
      );

      // Sends the response back to the client
      res.status(200).send();
    }
  );
});

app.listen(9000);

webpack.config.node.js

const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

// http://jlongster.com/Backend-Apps-with-Webpack--Part-I
let nodeModules = {};
fs
  .readdirSync('node_modules')
  .filter(function(f) {
    return f.charAt(0) !== '.';
  })
  .forEach(function(f) {
    nodeModules[f] = 'commonjs ' + f;
  });

module.exports = {
  target: 'node',
  entry: './server.js',
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'server.js',
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015', 'react', 'stage-0'],
        },
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: 'css-loader',
      },
      {
        test: /\.(gif|png|jpe?g|svg)$/i,
        use: [
          'file-loader',
          {
            loader: 'image-webpack-loader',
            options: {
              bypassOnDebug: true,
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new webpack.DefinePlugin({
      PRODUCTION: JSON.stringify(true),
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true,
    }),
    //new UglifyJSPlugin(),
  ],
  externals: nodeModules,
};

最后這是我的 app.js

import React from 'react';
import { hydrate, render } from 'react-dom'; // eslint-disable-line
import { Provider } from 'react-redux';
import { BrowserRouter, Route } from 'react-router-dom';
import configureStore from './store/configureStore';
import Home from './routes/home';
import Profile from './routes/profile';
import Contact from './routes/contact';
import RouterWrapper from './routeWrapper';
import './index.css';

const store = configureStore();
window.store = store;

// Does the user's browser support the HTML5 history API?
// If the user's browser doesn't support the HTML5 history API then we
// will force full page refreshes on each page change.
const supportsHistory =
  window && window.history && 'pushState' in window.history ? true : false;

const routes = (
  <div>
    <RouterWrapper>
      <Route exact path="/" component={Home} />
      <Route path="/profile" component={Profile} />
      <Route path="/contact" component={Contact} />
    </RouterWrapper>
  </div>
);

const app = (
  <Provider store={store}>
    <BrowserRouter forceRefresh={!supportsHistory}>{routes}</BrowserRouter>
  </Provider>
);

const root = document.getElementById('root');

const renderApp = newRoot => {
  if (process.env.NODE_ENV === 'development') {
    render(app, newRoot);
  } else {
    hydrate(app, newRoot);
  }
};

renderApp(root);

export default routes;

根本原因:插件style-loader會在js中輸入css,生成js中的樣式標簽,比如document.createelement ("style") 但服務器沒有文檔 api。 所以它崩潰了。

嘗試使用mini-css-extract-plugin從 js 文件中mini-css-extract-plugin CSS 代碼。 避免在js文件中使用document.creatEelement ("style")
貼一段我的配置

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  mode: process.env.NODE_ENV === 'development' ? 'development' : 'production',
  module: {
    rules: [
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
        // use: ['style-loader', 'css-loader']
      }
    ],
  },
  plugins: [new MiniCssExtractPlugin()],

};

如果您有任何問題,請告訴我

只需將您的routes移動到不同的文件。 導入它們時,會執行整個文件,從而導致錯誤。

導入文件時,將執行文件的全部內容,包括const root = document.getElementById('root'); 線。

如果您將routes聲明移動到一個單獨的文件並從那里導出,您應該能夠在server.jsapp.js導入該文件,您應該沒問題。

暫無
暫無

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

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