简体   繁体   中英

React SSR ReferenceError: document is not defined

I am having trouble to render react server side. I always get ^

ReferenceError: document is not defined

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

I know what is happing that node dosen't understand browsers document object. But I cant seem to fix it. Am I doing something wrong in webpack or on the server or in app.js.

here is my code

server.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,
};

And finally here is my 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;

Root cause: the plugin style-loader will type CSS into js to generate the style label in js, such as document.createelement ("style") ; but the server doesn't have the document api. So it crashed.

Try using the mini-css-extract-plugin to pull the CSS code out of the js file. Avoid using document.creatEelement ("style") in js file;
Post a section of my configuration

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()],

};

Please let me know if you have any questions

Just move your routes to a different file. When you import them, the whole file is executed, causing the error.

When you import a file, the entire contents of the file will be executed, including the const root = document.getElementById('root'); line.

If you move the routes declaration to a separate file and export it from there, you should be able to import that file both in server.js and in app.js you should be fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM