Why is React Chunking not working? - reactjs

I am trying to chunk my react app using webpack, but nothing loads and it says vendor.js says 0 bytes. What am I doing wrong? I think there is something wrong in my index.html. vendor.js and index.js are both located in the src folder. I've already tried following this, but no luck https://github.com/webpack/webpack/issues/368
Webpack
var webpack = require('webpack');
module.exports = {
entry: {
app: './src/index.js',
vendor: './src/vendor.js'
},
output: {
path: __dirname,
publicPath: '/',
filename: '[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor.js')
],
module: {
loaders: [{
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react', 'es2015', 'stage-1']
}
}]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
devServer: {
historyApiFallback: true,
contentBase: './'
}
};
index.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style/style.css">
</head>
<body>
<div id="app"></div>
</body>
<script src="/bundle.js"></script>
</html>

You should add all of your common dependencies into vendor.js via import,
e.g jquery, lodash, post-css, jss or any others which you are using.
since you have not done that may be because of that your vendor.js file is of size 0 bytes.
Change your CommonsChunkPlugin to
new webpack.optimize.CommonsChunkPlugin({
name : 'vendor',
}),
Also you need to change your index.html
because in your webpack config you have
output: {
path: __dirname,
publicPath: '/',
filename: '[name].js'
},
Here
filename: '[name].js'
means files will be generated by webpack based on entry names as app.js and vendor.js
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/style/style.css">
</head>
<body>
<div id="app"></div>
</body>
<script src="/vendor.js"></script>
<script src="/app.js"></script>
</html>

change new webpack.optimize.CommonsChunkPlugin('vendor.js') to
new webpack.optimize.CommonsChunkPlugin('vendor','vendor.js', Infinity)
or better be new webpack.optimize.CommonsChunkPlugin('vendor','vendor.[chunkhash].js', Infinity)
I would recommend migrating to Webpack 2. Refer migrating guide

Related

Webpack doesn't work in prod but in dev env is fine

I am serving with NodeJS react as static but index.html cant render bundle.js file (in network tab it says that bundle.js is Not found) ;
Express looks like this:
app.get(/^\/ui\/(?!images|vendor|assets|packages)/, function (req, res, next) {
res.sendFile(path.join(__dirname, 'public/ui/index.html'))
});
app.use(express.static(path.join(__dirname, 'public')));
app.listen(port, function () {
console.log('App listening on port ' + port);
});
The folder structure looks like this:
the index html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Font Awesome CDN-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css"
integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g=="
crossorigin="anonymous" referrerpolicy="no-referrer"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Skenario Labs - Banking</title>
</head>
<body>
<div id="root"></div>
<script src="/bundle.js"></script>
</body>
Webpack common :
module.exports = {
entry: path.resolve(__dirname, '..', '/src/index.tsx'),
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader"
]
},
{
test: /\.(woff(2)?|eot|ttf|otf|)$/,
type: 'asset/inline',
},
{
test: /\.(png|jpe?g|gif|jp2|webp|ico|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
output: {
path: path.resolve(__dirname, '..', './build'),
filename: 'bundle.js',
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '..', './public/ui/index.html'),
favicon: "./public/ui/assets/images/favicon.png",
filename: 'index.html',
inject: 'body'
}),
],
stats: 'errors-only',
}
Ive tried to use it both ways :
<script src="/bundle.js"></script>
<script src="bundle.js"></script>
But then getting status 304 and the bundle.js inside shows white screen instead of JS

single spa react application chunk load only when needed

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?

webpack builds html but doesn't display any javascript

So i'm mostly new to javascript and current i'm having a problem displaying the fruits of our labor. We have a webpack build from our React/Redux and although it displays css it doesn't contain any of the JS we've written. Any help would be greatly appreciated. I'm sure i'm missing something simple but i would greatly appreciate any help.
Here is the html file produced:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Talent Identification Manager</title>
<link href="main.css" rel="stylesheet"></head>
<body>
<div id="root"></div>
<script type="text/javascript" src="main.bundle-0.0.1.js"></script></body>
</html>
So here is the webpack configuration:
'let path = require('path');
let webpack = require('webpack');
const publicPath = '/dist/build/';
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
//Content
entry: './src/index.js',
mode: 'development',
// A SourceMap without column-mappings ignoring loaded Source Maps.
devtool: 'cheap-module-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('development')
}
}),
//simplifies creation of HTML files to serve your webpack bundles. This is especially useful for webpack bundles that include a hash in the filename which changes every compilation. You can either let the plugin generate an HTML file for you, supply your own template using lodash templates or use your own loader.
new HtmlWebpackPlugin({
title: 'Talent Identification Manager'
}),
//Auto replacement of page when i save some file, even css
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
output: {
path: path.join(__dirname, publicPath),
filename: 'main.bundle-0.0.1.js',
publicPath: "",
sourceMapFilename: 'main.map',
},
devServer: {
port: 3000,
host: 'localhost',
//Be possible go back pressing the "back" button at chrome
historyApiFallback: true,
noInfo: false,
stats: 'minimal',
publicPath: publicPath,
contentBase: path.join(__dirname, publicPath),
//hotmodulereplacementeplugin
hot: true
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules(?!\/webpack-dev-server)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-2'],
plugins: ['syntax-decorators']
}
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
}'
I feel this should be like this
output: {
path: path.join(__dirname, publicPath),
filename: 'main.bundle-0.0.1.js',
},
plus give relative path in html file

using "externals" in "webpack" for loading from CDN

I'm new in reactjs and webpack.
I want to load a library from CDN in my project using webpack external, like sample in https://webpack.js.org/configuration/externals/#externals
but it doesn't work.
can you help me?
It's my index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="stylesheet" href="https://cdn.cdncode.com/twitter- bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.cdncode.com/bootstrap-rtl/3.2.0-rc2/css/bootstrap-rtl.min.css"/>
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
</head>
<body class="content_main">
<div id="root"></div>
<script type="text/javascript" src="https://cdn.cdncode.com/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.cdncode.com/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script type="text/javascript" src="https://cdn.cdncode.com/html5shiv/r29/html5.min.js"></script>
<script type="text/javascript" src="https://cdn.cdncode.com/respond.js/1.4.2/respond.min.js"></script>
</script>
</body>
</html>
and it's my webpack.config.js
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: "./src/index.js",
output: {
path: "/js/",
publicPath: 'http://jwplayer.k-cdn.net/',
filename: "bundle.js"
},
module: {
loaders: [
{test: /\.js$/, loader: 'babel-loader', query: {presets: ['es2015', 'react']}},
{test: /\.css$/, loader: 'style-loader!css-loader'},
{test: /\.(png|svg|jpg|gif)$/, loader: 'file-loader'},
{test: /\.(woff|woff2|eot|ttf|otf)$/, loader: 'file-loader'}
]
},
resolve: {
extensions: ['.js']
},
externals: {
jquery: 'jQuery'
}
};
To execute $ function of JQuery into React, i did following steps.
1) Install JQuery from npm
npm install --save jquery
2) Changes in webpack.config.js, Remove external, add plugins
module.exports = {
entry: "./src/index.js",
output: {
path: "/js/",
publicPath: 'http://jwplayer.k-cdn.net/',
filename: "bundle.js"
},
module: {
loaders: [
{test: /\.js$/, loader: 'babel-loader', query: {presets: ['es2015', 'react']}},
{test: /\.css$/, loader: 'style-loader!css-loader'},
{test: /\.(png|svg|jpg|gif)$/, loader: 'file-loader'},
{test: /\.(woff|woff2|eot|ttf|otf)$/, loader: 'file-loader'}
]
},
resolve: {
extensions: ['.js']
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
}),
]
};
Now we can use $ in React

Webpack React library to global variable in window

I use webpack to build React components.
Here is the JSX file:
// index.js
var React = require('react');
var ReactDOM = require('react-dom');
var renderHelloComponnet = function(id) {
ReactDOM.render(
<h1>Hello, World!</h1>,
document.getElementById(id)
);
}
Then, I want to call the function renderHelloComponnet in html
I have the config file for webpack:
module.exports = {
entry: {
index: './src/index.js'
},
output: {
path: __dirname + '/build',
filename: '[name].bundle.js',
libraryTarget: "var",
library: ["ABC"],
},
resolve: {
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: { presets:['react'] }
}
]
}
}
However I can't render my component in html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script type="text/javascript" src="./build/index.bundle.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/javascript">
ABC.renderHelloComponnet('example');
</script>
</body>
</html>
I get the error: TypeError: ABC.renderHelloComponnet is not a function
What you export in your module.exports is what you get in your library var. So if in your index.js you export renderHelloComponent:
module.exports = renderHelloComponent;
then in your ABC var you'll get the function:
<script type="text/javascript">
ABC('example');
</script>
If you want your library var to actually have the renderHelloComponent method you can export an object with this function inside:
module.exports = {
renderHelloComponent: renderHelloComponent
}
Then you will be able to do this:
<script>
ABC.renderHelloComponent('example');
</script>

Resources