I have this problem where my react production build shows a blank page when served with simple node server. I have tried to tackle this problem different ways but cant find a solution to this. Any suggestions how to fix this problem would be much appreciated.
I am using BrowserRouter as the routing option on client side.
Here is my webpack.common.js code for the production build:
const path = require("path");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const outputPath = path.join(__dirname, "dist");
const port = process.env.PORT || 8000;
module.exports = {
context: __dirname,
entry: {
main: ["#babel/polyfill", "./src/App.js"]
},
output: {
path: path.join(__dirname, "dist"),
filename: "bundle.js"
},
resolve: {
modules: ["node_modules", "./src"],
extensions: [".js", ".jsx"]
},
module: {
rules: [
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
use: "css-loader!sass-loader"
})
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: "css-loader"
})
},
{
test: /\.(js|jsx)$/,
loader: "babel-loader",
exclude: /node_modules/
},
{
test: /\.(jpg|jpeg|png|gif|mp3|svg)$/,
loaders: ["file-loader"]
},
{
test: /\.html$/,
use: [
{
loader: "html-loader"
}
]
}
]
},
plugins: [
new ExtractTextPlugin("bundle.css"),
new HtmlWebpackPlugin({
baseUrl: process.env.NODE_ENV == 'development' ? '/' : '/',
filename: "index.html",
template: path.join(__dirname, "./public/index.html")
})
],
devServer: {
port,
historyApiFallback: true,
publicPath: "/"
}
};
Here is the entry for the compiler to run the production build:
const webpack = require('webpack');
const merge = require('webpack-merge');
const webpackCommonConfig = require('./webpack.config.common');
module.exports = merge(webpackCommonConfig, {
plugins: [
new webpack.EnvironmentPlugin({ NODE_ENV: 'production' }),
],
devtool: "source-map",
devServer: {
compress: true,
},
});
Here is the code for node server serving the application:
const express = require('express')
const path = require('path')
const port = process.env.PORT || 8000
const fs = require('fs');
const app = express()
app.get(['/bundle.css', '/bundle.css.map'], (req, res) => {
res.writeHead(200, {'Content-Type': 'text/css'});
fs.createReadStream(path.resolve(__dirname, `../dist/${req.url}`)).pipe(res);
})
app.get(['/bundle.js', '/bundle.js.map'], (req, res) => {
res.writeHead(200, {'Content-Type': 'text/javascript'});
fs.createReadStream(path.resolve(__dirname, `../dist/${req.url}`)).pipe(res);
})
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, '../dist', 'index.html'))
})
app.listen(port)
console.log("server started on port " + port)
Here is the index.html that is compiled to dist folder:
<!DOCTYPE html>
<html lang="en" style="height:100%">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.gif">
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<title>Zoi</title>
<link href="bundle.css" rel="stylesheet"></head>
<body style="height:100%">
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script type="text/javascript" src="bundle.js"></script></body>
</html>
Related
I have a React app that I used Webpack to deploy it. The webpack.config.js file is as below:
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const InterpolateHtmlPlugin = require('interpolate-html-plugin');
let mode = "development"
let target = "web";
if(process.env.NODE_ENV === "production") {
mode = "production";
target = "browserslist";
}
module.exports = {
entry: './src/index.js',
mode: mode,
target: target,
output: {
path: path.resolve(__dirname, "dist"),
assetModuleFilename: "images/[hash][ext][query]"
},
stats: {
children: true,
errorDetails: true
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin(),
new HtmlWebpackPlugin({
template: "./public/index.html"
}),
new InterpolateHtmlPlugin({PUBLIC_URL: '/' })
],
resolve: {
extensions: [".js", ".jsx"]
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg|ico)$/i,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 30 * 1024,
}
}
},
{
test: /\.(s[ac]|c)ss$/i,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: ""
}
},
"css-loader",
"postcss-loader",
"sass-loader"]
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
}
}
]
},
devtool: "source-map",
devServer: {
port: 3000,
allowedHosts: "all",
static: "./dist",
hot: true
},
};
and the index.html file enjoys %PUBLIC_URL%:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<base href="%PUBLIC_URL%/" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>PSWebsite</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
The npm run build command runs successfully but the npm start returns blank page
I have built a single spa react app, below is the index.html for the app
there are 2 application, app1 and app2, app1 loads initially, and app2 loads only when needed
index.html for single spa config of app1 and app2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=9;IE=10;IE=Edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Project</title>
<!-- app2 css -->
<link href='http://localhost:8003/app2.css' rel='stylesheet'/>
</head>
<body>
<script>
function randstr(prefix) {
return Math.random().toString(36).replace('0.', prefix || '');
}
var suffix = randstr('?v=');
document.write('<script src="config/config.js' + suffix + '"><\/script>');
document.write('<script type="systemjs-importmap" crossorigin="anonymous">' +
'{ \n"imports": {\n' +
' "#portal/config": "http://localhost:8001/index.js' + '",\n' +
' "#portal/app1": "http://localhost:8002/app1.js' + '",\n' + // this loads first, it has login page
' "#portal/app2": "http://localhost:8003/app2.js' + '" \n }\n}' + // app 2 which loads on click
'<\/script>');
</script>
<!-- load the vendor chunk of app2-->
<script src='http://localhost:8003/vendor.app2.js'></script>
<!-- Load the common deps-->
<script>
imports and loads dependencies of react, react-dom, lodash and others
</script>
<!-- Load the application -->
<script>
SystemJS.import('#portal/config')
</script>
<div id="app" class="site-container"></div>
</body>
</html>
in the above index.html, configured the apps by systemjs-importmap and everything works great.
As you can see in the importmap, importing the app2("#portal/app2": "http://localhost:8003/app2.js'), this will load only when needed, but this bundle is bit huge and i have tried to improve the performance by extracting the css and vendor chunk, minifying the js and css, which indeed improved the performance, those scripts are added as well (http://localhost:8003/vendor.app2.js , http://localhost:8003/app2.css)
webpack.config.js for app2
const webpack = require('webpack')
const path = require('path')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
entry: path.resolve(__dirname, 'src/app2.js'),
output: {
filename: 'app2.js',
library: 'app2',
libraryTarget: 'amd',
path: path.resolve(__dirname, 'build/app2'),
},
mode: 'production',
module: {
rules: [
{parser: {System: false}},
{
test: /\.js$/,
exclude: [path.resolve(__dirname, 'node_modules')],
loader: 'babel-loader',
options: {
presets: ["#babel/preset-env"]
}
},
{
test: /\.krem.css$/,
exclude: [path.resolve(__dirname, 'node_modules')],
use: [
{
loader: 'kremling-loader',
options: {
namespace: 'app2',
postcss: {
plugins: {
'autoprefixer': {},
},
},
},
},
],
},
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url-loader' },
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url-loader' },
// { test: /\.css$/, loader: "style-loader!css-loader"},
{ test: /\.css$/, use: [
MiniCssExtractPlugin.loader,
'css-loader',
]},
],
},
plugins: [
new CleanWebpackPlugin({
verbose: true,
cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, 'build/app2')],
}),
new CopyWebpackPlugin({
patterns: [
{from: path.resolve(__dirname, 'src/ppsr.js')},
]}),
new TerserPlugin({
parallel: true,
cache: true
}),
new MiniCssExtractPlugin({
filename: '[name].css',
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
name: 'vendor',
chunks: 'all',
reuseExistingChunk: true,
priority: 1,
test: module =>
/[\\/]node_modules[\\/]/.test(module.context),
minChunks: 1,
minSize: 0,
},
},
},
minimize: true,
minimizer: [
new CssMinimizerPlugin(),
],
},
devtool: 'source-map',
externals: [
/^#portal\/*/,
/^lodash$/,
/^single-spa$/,
/^rxjs\/?.*$/,
/^react$/,
/^react\/lib.*/,
/^react-dom$/,
/.*react-dom.*/,
],
}
I get the build files vendor.app2.css, app2.css , app2.js files...only js loads when needed because it is imported by importmap, and the css and vendor are in global space(means loads irrespective of app2 is loaded or not)
Question:
How can I load the vendor and CSS same as js for app2 only when needed in single spa react?
in my react app i'm using webpack 4,before implementing split chunk everything is working fine but after splitChunks it generating vendor.js file and it showing on html file also but page is blank.
my webpack file
const webpack = require('webpack');
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WebpackMd5Hash = require('webpack-md5-hash');
const CompressionPlugin = require('compression-webpack-plugin');
const VENDOR_LIBS =[
'antd','axios','moment','rc-time-picker','react',
'react-dom','react-ga','react-google-maps','react-loadable',
'react-redux','react-router','react-router-dom','recompose','redux','redux-thunk'
];
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
entry:{
vendor: VENDOR_LIBS,
main: './src/app.js',
},
output: {
path: path.join(__dirname, 'public'),
filename: '[name].chunkhash.bundle.js',
chunkFilename: '[name].chunkhash.bundle.js',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.s?css$/,
use: ['style-loader', MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
},{
test: /\.(gif|svg|jpg|png|ttf|eot|woff(2)?)(\?[a-z0-9=&.]+)?$/,
loader: "file-loader",
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'style.[contenthash].css',
}),
new HtmlWebpackPlugin({
inject: false,
hash: true,
template: './src/index.html',
filename: 'index.html'
}),
new WebpackMd5Hash(),
new CompressionPlugin({
algorithm: 'gzip',
test : /\.js$|\.css$|\.eot?.+$|\.ttf?.+$|\.woff?.+$|\.svg?.+$/,
threshold: 10240,
minRatio: 0.8
}),
],
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
chunks: 'initial',
name: 'vendor',
test: 'vendor',
enforce: true
},
}
},
runtimeChunk: true,
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true
}),
new OptimizeCSSAssetsPlugin({})
],
},
};
and in my /src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
<link rel="stylesheet" href="<%=htmlWebpackPlugin.files.chunks.main.css %>">
<title>Book Ambulance - Stanplus</title>
</head>
<body>
<div id="app">
</div>
<script src="<%= htmlWebpackPlugin.files.chunks.main.entry %>"></script>
<script src="<%= htmlWebpackPlugin.files.chunks.vendor.entry %>"></script>
</body>
</html>
you can that those files are loading in browser but page is blank
i have to questions
a) why my app not showing nothing even not any error(may be something in output,splitChunks or in html page).
b) after doing UglifyJsPlugin,CompressionPlugin and dynamically adding routes and import libs/files but also my vendor.js file size is 580 KB,except all these techniques how can i reduce the size of my vendor.js file
I am currently trying to learn ng2.
I have followed the tutorial here : https://angular.io/docs/ts/latest/guide/webpack.html
And have found creating a dist of my build, upon attempting to run it, both locally and on Github Pages, I am receiving a 404 error on my bundle files.
I have followed the tutorial line for line and it does not appear to be working.
Can anyone help?
My webpack build is as follows :
webpack.common.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');
module.exports = {
entry: {
'polyfills': './src/polyfills.ts',
'vendor': './src/vendor.ts',
'app': './src/main.ts'
},
resolve: {
extensions: ['', '.js', '.ts']
},
module: {
loaders: [
{
test: /\.ts$/,
loaders: ['awesome-typescript-loader', 'angular2-template-loader']
},
{
test: /\.html$/,
loader: 'html'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file?name=assets/[name].[hash].[ext]'
},
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
},
{
test: /\.css$/,
include: helpers.root('src', 'app'),
loader: 'raw'
}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'vendor', 'polyfills']
}),
new HtmlWebpackPlugin({
template: 'src/index.html'
})
]
};
webpack.prod.js
var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
module.exports = webpackMerge(commonConfig, {
devtool: 'source-map',
output: {
path: helpers.root('dist'),
publicPath: '/',
filename: '[name].[hash].js',
chunkFilename: '[id].[hash].chunk.js'
},
htmlLoader: {
minimize: false // workaround for ng2
},
plugins: [
new webpack.NoErrorsPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
mangle: {
keep_fnames: true
}
}),
new ExtractTextPlugin('[name].[hash].css'),
new webpack.DefinePlugin({
'process.env': {
'ENV': JSON.stringify(ENV)
}
})
]
});
Index.html
<!DOCTYPE html>
<html>
<head>
<base href=/>
<title>Angular With Webpack</title>
<meta charset=UTF-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<link href="/app.f053dbe7ce9dd32c3e43.css" rel="stylesheet">
</head>
<body>
<my-app>Loading...</my-app>
<script type="text/javascript" src="/polyfills.f053dbe7ce9dd32c3e43.js"></script>
<script type="text/javascript" src="/vendor.f053dbe7ce9dd32c3e43.js"></script>
<script type="text/javascript" src="/app.f053dbe7ce9dd32c3e43.js"></script>
</body>
</html>
I had the same problem! Do you have karma.conf.js in config dir? Your project structure should be like :
I'm trying to use react-hot-loader with webpack-dev-server and react-router, but when I try to access localhost:3000/ I get :
Cannot GET /
Of course, it works when I try to access localhost:8000/. I tried to follow react-hot-boilerplate, without success.
Here's my code:
server.js
const http = require('http');
const express = require('express');
const consolidate = require('consolidate');
const bodyParser = require('body-parser');
const routes = require('./routes');
const app = express();
app.set('views', 'public/pages'); // Set the folder-name from where you serve the html page.
app.set('view engine', 'html');
app.engine('html', consolidate.handlebars);
app.use(express.static('public')); // Set the folder from where you serve all static files
app.use(express.static('public/dist')); // Set the folder from where you serve all static files
app.use(bodyParser.urlencoded({ extended: true }));
const portNumber = 8000;
http.createServer(app).listen(portNumber, () => {
console.log(`Server listening at port ${portNumber}`);
routes.initialize(app);
});
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const config = require('./webpack.config');
new WebpackDevServer(webpack(config), {
colors: true,
historyApiFallback: true,
inline: true,
hot: true,
}).listen(3000, 'localhost', (err) => {
if (err) {
console.log(err);
}
});
routes.js (so all the routes point to the router)
function initialize(app) {
const routes = [
'/',
'/login',
];
routes.forEach((route) => {
app.get(route, (req, res) => {
res.render('main-content.html');
});
});
}
exports.initialize = initialize;
webpack.config.js
const webpack = require('webpack');
const path = require('path');
const nodeDir = `${__dirname}/node_modules`;
const config = {
resolve: {
alias: {
react: `${nodeDir}/react`,
'react-dom': `${nodeDir}/react-dom`,
'react-router': `${nodeDir}/react-router`,
'react-bootstrap': `${nodeDir}/react-bootstrap`,
velocity: `${nodeDir}/velocity-animate`,
moment: `${nodeDir}/moment`,
slimscroll: `${nodeDir}/slimscroll`,
},
},
entry: {
routes: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./public/src/routes/js/main',
],
vendors: [
'react', 'react-dom', 'react-router', 'react-bootstrap',
'velocity', 'moment', 'slimscroll',
],
},
output: {
path: path.join(__dirname, 'public/dist'),
publicPath: path.join(__dirname, 'public/dist'),
filename: 'bundles/[name].bundle.js',
chunkFilename: 'chunks/[name].chunk.js',
},
module: {
loaders: [
{
test: /\.jsx?$/,
include: path.join(__dirname, 'public'),
loader: 'react-hot',
},
{
test: /\.js$/,
include: path.resolve(__dirname, 'public'),
loader: 'babel',
},
{
test: /\.css$/,
include: path.join(__dirname, 'public'),
loader: 'style!css-loader?modules&importLoaders=1' +
'&localIdentName=[name]__[local]___[hash:base64:5]',
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.CommonsChunkPlugin('vendors', './bundles/vendors.js', Infinity),
],
};
module.exports = config;
scripts
"scripts": {
"dev": "webpack --config webpack.config.js",
"hot": "webpack-dev-server --devtool eval --progress --colors --inline --hot",
"build": "webpack -p --config webpack.config.prod.js"
}
main-content.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Brigad Admin Panel</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.0/css/font-awesome.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<!-- Customs -->
<link rel="stylesheet" href="styles/global.css">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,600,400italic,700,600italic,700italic,800,800italic&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
</head>
<body class="hold-transition">
<div id="content"></div>
<!--<script src="dist/bundles/vendors.js"></script>-->
<!--<script src="dist/bundles/routes.bundle.js"></script>-->
<script src="http://localhost:8080/public/dist/bundles/vendors.js"></script>
<script src="http://localhost:8080/public/dist/bundles/routes.bundle.js"></script>
</body>
</html>
entry point
import React from 'react';
import { render } from 'react-dom';
import { Router, browserHistory } from 'react-router';
import RootRoute from './components/RootRoute';
render(
<Router history={browserHistory} routes={RootRoute} />,
document.getElementById('content')
);
How can I make react-hot-loader to work?
Thanks in advance.
You should provide localhost:3000 for publicPath in webpack.config.
In the dev-server config you maybe need to add contentBase option pointing to your build output (./public/dist).
Take a look at this https://github.com/webpack/docs/wiki/webpack-dev-server#content-base