简体   繁体   中英

Deploying React App with webpack and Express.js

I'm developping an application using React, and webpack as the module bundler for the client part. For the server, I'm using Express.js.

For the client, I'm using webpack.config.dev.js for the configuration, and I use npm start to start a web server at localhost:3000.

For the server, I have the app.js file, and I use node bin/www to start the express server, but I have to give a different port to start the server, in this case, localhost:3100.

I have only a package.json for both, client, and server.

Is there a way to deploy both client and server in the same domain? If so, how can I do it? I would like from my client code to reference the endpoint as: /data/users, instead of doing localhost:3100/data/users.

See webpack config, express config, and package.json below (in case it helps):

webpack.config.dev.js:

import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import autoprefixer from 'autoprefixer';
let node_dir = __dirname + '/node_modules';
let app_dir = __dirname + '/app';

export default {
  resolve: {
    extensions: ['', '.js', '.jsx'],
    alias: {
      'jquery': node_dir + '/jquery/dist/jquery.js',
      'jquery-validation': node_dir + '/jquery-validation/dist/jquery.validate.js',
      'jquery-placeholder': node_dir + '/jquery-placeholder/jquery.placeholder.js',
      'jquery-oauth': node_dir + '/jquery-oauth/dist/jquery.oauth.js',
      'bootstrap': node_dir + '/bootstrap/dist/js/bootstrap.min.js',
      'owlcarousel':node_dir+'/owlcarousel/owl-carousel/owl.carousel.min.js',
      'modernizr':node_dir + '/npm-modernizr/modernizr.js',
      'jquery-shuffle':node_dir + '/shufflejs/dist/shuffle.js' ,
      'jquery-stellar':node_dir + '/jquery.stellar/jquery.stellar.js',
      'jquery-touchswipe':node_dir + '/jquery-touchswipe/jquery.touchSwipe.min.js'
    },
    modulesDirectories: ['js',  'node_modules']
  },
  debug: true,
  devtool: 'cheap-module-eval-source-map', // more info:https://webpack.github.io/docs/build-performance.html#sourcemaps and https://webpack.github.io/docs/configuration.html#devtool
  noInfo: true, // set to false to see a list of every file being bundled.
  context: app_dir + "/app",
  entry: {
    // must be first entry to properly set public path
    app: [app_dir+'/webpack-public-path',
      'webpack-hot-middleware/client?reload=true',
      app_dir+'/index']
  },
  target: 'web', // necessary per https://webpack.github.io/docs/testing.html#compile-and-test
  output: {
   // path: `${__dirname}/src`, // Note: Physical files are only output by the production build task `npm run build`.
    path: `${__dirname}/app`,
    publicPath: '/',
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.ResolverPlugin(
      new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("package.json", ["main"])
    ),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('development'), // Tells React to build in either dev or prod modes. https://facebook.github.io/react/downloads.html (See bottom)
      __DEV__: true
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    new HtmlWebpackPlugin({     // Create HTML file that includes references to bundled CSS and JS.
                                //template: 'src/index.ejs',
      template: app_dir+'/index.html',
      minify: {
        removeComments: true,
        collapseWhitespace: true
      },
      inject: true
    }),
    new webpack.ProvidePlugin({
      $: "jquery",
      jquery: "jquery",
      "windows.jQuery": "jquery",
      jQuery:"jquery",

    }),
    new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js', Infinity)
  ],
  module: {
    loaders: [
      {test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel']},
      {test: /\.eot(\?v=\d+.\d+.\d+)?$/, loader: 'file'},
      {test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url?limit=10000&mimetype=application/font-woff"},
      {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream'},
      {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml'},
      {test: /\.(jpe?g|png|gif)$/i, loader: 'file?name=[name].[ext]'},
      {test: /\.ico$/, loader: 'file?name=[name].[ext]'},
      {test: /(\.css|\.scss)$/, loaders: ['style', 'css?sourceMap', 'postcss', 'sass?sourceMap']},

    ]
  },
  postcss: ()=> [autoprefixer]
};

app.js for the server side:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});


module.exports = app;

And package.json:

{
  "name": "dario-webapplication",
  "version": "1.0.0",
  "description": "Webapplication for Dario project",
  "engines": {
    "npm": ">=3"
  },
  "scripts": {
    "server": "nodemon ./server/bin/www",
    "preinstall": "node tools/nodeVersionCheck.js",
    "setup": "node tools/setup/setupMessage.js && npm install && node tools/setup/setup.js",
    "remove-demo": "babel-node tools/removeDemo.js",
    "start-message": "babel-node tools/startMessage.js",
    "prestart": "npm-run-all --parallel start-message remove-dist",
    "start": "npm-run-all --parallel open:app lint:watch server",
    "open:app": "babel-node tools/srcServer.js",
    "open:dist": "babel-node tools/distServer.js",
    "lint": "esw webpack.config.* app tools --color",
    "lint:watch": "npm run lint -- --watch",
    "clean-dist": "npm run remove-dist && mkdir dist",
    "remove-dist": "rimraf ./dist",
    "prebuild": "npm run clean-dist && npm run lint && npm run test",
    "build": "babel-node tools/build.js && npm run open:dist",
    "test": "mocha tools/testSetup.js \"app/**/*.spec.js\" --reporter progress",
    "test:cover": "babel-node node_modules/isparta/bin/isparta cover --root app --report html node_modules/mocha/bin/_mocha -- --require ./tools/testSetup.js \"app/**/*.spec.js\" --reporter progress",
    "test:cover:travis": "babel-node node_modules/isparta/bin/isparta cover --root app --report lcovonly _mocha -- --require ./tools/testSetup.js \"app/**/*.spec.js\" && cat ./coverage/lcov.info | node_modules/coveralls/bin/coveralls.js",
    "test:watch": "npm run test -- --watch",
    "open:cover": "npm run test:cover && open coverage/index.html"
  },
  "author": "Francisco Jose Parra Gonzalez",
  "license": "MIT",
  "dependencies": {
    "bootstrap": "3.3.7",
    "imports-loader": "0.6.5",
    "jquery": "^1.11.1",
    "jquery-oauth": "2.1.3",
    "jquery-placeholder": "2.3.1",
    "jquery-touchswipe": "1.6.15",
    "jquery-ui": "^1.10.4",
    "jquery-validation": "1.15.1",
    "jquery.easing": "^1.3.2",
    "jquery.stellar": "0.6.2",
    "lightgallery": "1.2.21",
    "npm-modernizr": "2.8.3",
    "object-assign": "4.1.0",
    "owlcarousel": "1.3.3",
    "react": "15.3.0",
    "react-bootstrap": "0.30.3",
    "react-dom": "15.3.0",
    "react-redux": "4.4.5",
    "react-router": "2.6.1",
    "react-router-redux": "4.0.5",
    "redux": "3.5.2",
    "redux-thunk": "2.1.0",
    "shufflejs": "4.0.1",
    "throttle-debounce": "1.0.1",
    "body-parser": "~1.15.1",
    "cookie-parser": "~1.4.3",
    "debug": "~2.2.0",
    "express": "~4.13.4",
    "express-validator": "^2.20.8",
    "jade": "~1.11.0",
    "morgan": "~1.7.0",
    "serve-favicon": "~2.3.0"
  },
  "devDependencies": {
    "autoprefixer": "6.4.0",
    "babel-cli": "6.11.4",
    "babel-core": "6.11.4",
    "babel-loader": "6.2.4",
    "babel-plugin-react-display-name": "2.0.0",
    "babel-plugin-transform-react-remove-prop-types": "0.2.9",
    "babel-preset-es2015": "6.9.0",
    "babel-preset-react": "6.11.1",
    "babel-preset-react-hmre": "1.1.1",
    "babel-preset-stage-1": "6.5.0",
    "babel-register": "6.11.6",
    "browser-sync": "2.14.0",
    "chai": "3.5.0",
    "chalk": "1.1.3",
    "connect-history-api-fallback": "1.2.0",
    "coveralls": "2.11.12",
    "cross-env": "2.0.0",
    "css-loader": "0.23.1",
    "enzyme": "2.4.1",
    "eslint": "3.2.2",
    "eslint-plugin-import": "1.12.0",
    "eslint-plugin-jsx-a11y": "2.0.1",
    "eslint-plugin-react": "6.0.0",
    "eslint-watch": "2.1.14",
    "extract-text-webpack-plugin": "1.0.1",
    "file-loader": "0.9.0",
    "html-webpack-plugin": "2.22.0",
    "isparta": "4.0.0",
    "mocha": "3.0.1",
    "mockdate": "1.0.4",
    "node-sass": "3.8.0",
    "npm-run-all": "2.3.0",
    "open": "0.0.5",
    "postcss-loader": "0.9.1",
    "prompt": "1.0.0",
    "react-addons-test-utils": "15.3.0",
    "redux-immutable-state-invariant": "1.2.3",
    "replace": "0.3.0",
    "rimraf": "2.5.4",
    "sass-loader": "4.0.0",
    "sinon": "1.17.5",
    "sinon-chai": "2.8.0",
    "style-loader": "0.13.1",
    "url-loader": "0.5.7",
    "webpack": "1.13.1",
    "webpack-dev-middleware": "1.6.1",
    "webpack-hot-middleware": "2.12.2",
    "webpack-md5-hash": "0.0.5"
  },
  "keywords": [],
  "repository": {
    "type": "git",
    "url": ""
  }
}

Maybe these boilerplate can help you. And here in line 37 is what you looking for.

What initially gets run is bin/server.js, which does little more than enable ES6 and ES7 awesomeness in the server-side node code. It then initiates server.js. In server.js we proxy any requests to /api/* to the API server, running at localhost:3030. All the data fetching calls from the client go to /api/*. Aside from serving the favicon and static content from /static, the only thing server.js does is initiate delegate rendering to react-router. At the bottom of server.js, we listen to port 3000 and initiate the API server.

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