简体   繁体   中英

Server side rendering with Webpack Express and React

I'm trying to learn React and after many attempts to run my configurations for React server-side rendering, I have reduced the code to the simplest example but I still can not understand why it does not work.

The Case

My controller:

$router = $container->get(Router::class);
$router->get('/test/{name:.*}', function ($request) use ($container) {
    /** @var $request \GuzzleHttp\Psr7\ServerRequest **/
    $client = new Client(['base_uri' => 'http://localhost:3000']);
    $response = $client->post('/', [
        'query' => ['module' => 'App'],
    ]);
    $contents = $response->getBody()->getContents();

    return $contents;
});

I'm using GuzzleHttp library

My server configuration:

/path/to/project/resources/server.js => /path/to/project/server.js

import express from 'express'
import React from 'react'
import ReactDOMServer from 'react-dom/server'

let app = express()

app.use('/', (request, response) => {
  try {
    let App = require('../public/dist/js/' + request.query.module)
    response.send(
      ReactDOMServer.renderToString(<App/>)
    )
  } catch (error) {
    response.status(500).send(error.message)
  }
})

app.listen(3000)

My component

/path/to/project/resources/assets/js/App.jsx => /path/to/project/public/dist/js/App.js

import React from 'react';

class App extends React.Component {
  render () {
    return <h1>Hello World!</h1>;
  }
}

module.exports = App;

My webpack config

const path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const UglifyJsPlugin = require ('uglifyjs-webpack-plugin');

function assets(path) {
  return './resources/assets/' + path;
}

const config = {
  entry: [
    assets('js/App.jsx'),
    assets('sass/app.scss')
  ],
  output: {
    path: path.resolve(__dirname, 'public/dist'),
    filename: 'js/App.js',
    publicPath: '/dist/'
  },
  resolve: {
    extensions: ['.js', '.jsx']
  },
  module: {
    rules: [
      { // es2015
        test: /\.(js|jsx)/,
        use: ['babel-loader'],
        exclude: [/node_modules/]
      },
      { // css / sass / scss loaders
        test: /\.(css|sass|scss)$/,
        use: ExtractTextPlugin.extract({
          use: ['css-loader', 'sass-loader'],
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: 'css/[name].bundle.css',
      allChunks: true
    }),
    new UglifyJsPlugin()
  ]
};

const server = {
  entry: [
    './resources/server.js'
  ],
  output: {
    path: path.resolve(__dirname, '.'),
    filename: 'server.js',
    publicPath: '/dist/'
  },
  resolve: {
    extensions: ['.js', '.jsx']
  },
  module: {
    rules: [
      { // es2015
        test: /\.(js|jsx)/,
        use: ['babel-loader'],
        exclude: [/node_modules/]
      }
    ]
  },
  plugins: [
    //new UglifyJsPlugin()
  ],
  target: 'node'
};

module.exports = [config, server];

Babelrc config

{
  "presets": ["es2015", "react"]
}

Result

  1. Error I get with Guzzle

500 Internal Server Error response: element.type.toLowerCase is not a function

  1. Error I get with nodeJS

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. in Unknown

  1. What I get when I bundle my files:

Webpack output

I tried a lot of different things but i can't get why this is throwing an error.


Edit:

With this configuration, I am able to make things work properly, the problem seems to come from the import part:

import express from 'express'
import * as React from 'react'
import ReactDOMServer from 'react-dom/server'

let app = express()

class App extends React.Component {
  render () {
    return <h1>Hello World!</h1>;
  }
}

const component = <App />;

app.use('/', (request, response) => {
  try {
    response.send(
      ReactDOMServer.renderToString(component)
    )
  } catch (error) {
    response.status(500).send(error.message)
  }
})

app.listen(3000)

Edit 2:

Here is logs that I retreive from .npm

0 info it worked if it ends with ok
1 verbose cli [ '/usr/bin/node', '/usr/bin/npm', 'start' ]
2 info using npm@5.6.0
3 info using node@v8.9.4
4 verbose run-script [ 'prestart', 'start', 'poststart' ]
5 info lifecycle Framework@1.0.0~prestart: Framework@1.0.0
6 info lifecycle Framework@1.0.0~start: Framework@1.0.0
7 verbose lifecycle Framework@1.0.0~start: unsafe-perm in lifecycle true
8 verbose lifecycle Framework@1.0.0~start: PATH: /usr/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/home/dev/www/node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
9 verbose lifecycle Framework@1.0.0~start: CWD: /home/dev/www
10 silly lifecycle Framework@1.0.0~start: Args: [ '-c', 'node server.js' ]
11 silly lifecycle Framework@1.0.0~start: Returned: code: 1  signal: null
12 info lifecycle Framework@1.0.0~start: Failed to exec start script
13 verbose stack Error: Framework@1.0.0 start: `node server.js`
13 verbose stack Exit status 1
13 verbose stack     at EventEmitter.<anonymous> (/usr/lib/node_modules/npm/node_modules/npm-lifecycle/index.js:285:16)
13 verbose stack     at emitTwo (events.js:126:13)
13 verbose stack     at EventEmitter.emit (events.js:214:7)
13 verbose stack     at ChildProcess.<anonymous> (/usr/lib/node_modules/npm/node_modules/npm-lifecycle/lib/spawn.js:55:14)
13 verbose stack     at emitTwo (events.js:126:13)
13 verbose stack     at ChildProcess.emit (events.js:214:7)
13 verbose stack     at maybeClose (internal/child_process.js:925:16)
13 verbose stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:209:5)
14 verbose pkgid Framework@1.0.0
15 verbose cwd /home/dev/www
16 verbose Linux 4.9.0-4-amd64
17 verbose argv "/usr/bin/node" "/usr/bin/npm" "start"
18 verbose node v8.9.4
19 verbose npm  v5.6.0
20 error code ELIFECYCLE
21 error errno 1
22 error Framework@1.0.0 start: `node server.js`
22 error Exit status 1
23 error Failed at the Framework@1.0.0 start script.
23 error This is probably not a problem with npm. There is likely additional logging output above.
24 verbose exit [ 1, true ]

In your component try.

import * as React from 'react';

class App extends React.Component {
  render () {
    return <h1>Hello World!</h1>;
  }
}

module.exports = App;

Could just be picking up the default export from the react package.

By default Webpack does not output CommonJS exports like you are expecting.

You could try to add

output: {
  ....
  library: 'server',
  libraryTarget: 'commonjs2'
}

target: 'node'

to the output module in Webpack. And just in case you want to import any sass (or any other files in your App.js) make sure they are referenced relatively to their source. They will be bundled up in the bundle that is created. So don't include your App.sass in your entries.

Hope that helps.

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