Webpack and Lighthouse performance test - reactjs

Hy,
I'm new to webpack and after hard learning I know that I know nothing :-).
I'm using React and I have 2 questions:
How can I run my build version in the browser, for the lighthouse test. At the moment I'm using npm run dist (below package.json). Which is working but I have the feeling it is not the correct way and my dist folder gets deleted. If I use npx create-react-app I can use serve -s build therefor.
If I make my Lighthouse performance test I get "Enable text compression". So I installed the compression-webpack-plugin and brotli-webpack-plugin. I have now in the dist folder the br and gz files but in the HTTP response header I don't get Content-Encoding: br or gzip and lighthouse still blames me for this.
package.jsong
"scripts": {
"start": "webpack-dev-server --config webpack.dev.js --open --progress --colors --hot",
"dist": "webpack-dev-server --config webpack.prod.js --open --mode production",
"build": "webpack --config webpack.prod.js"
}
webpack.common.js
const HtmlWebPackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
entry: {
main: "./src/index.js"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {
attributes: true
}
}
]
},
{
test: /\.(svg|png|jpg|gif|webp|jpeg)$/,
use: {
loader: "file-loader",
options: {
name: "[name].[hash].[ext]",
outputPath: "imgs"
}
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
})
]
};
webpack.dev.js
var path = require("path");
const { merge } = require("webpack-merge");
const common = require("./webpack.common");
module.exports = merge(common, {
mode: "development",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].bundle.js",
publicPath: "/"
},
devServer: {
historyApiFallback: true,
contentBase: "./dist"
}
});
webpack.prod.js
var path = require("path");
const { merge } = require("webpack-merge");
const common = require("./webpack.common");
const CompressionPlugin = require("compression-webpack-plugin");
const BrotliPlugin = require("brotli-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = merge(common, {
mode: "production",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[hash].bundle.js"
},
devtool: "source-map",
performance: {
hints: false
},
optimization: {
splitChunks: {
chunks: "all"
},
minimizer: [
new TerserPlugin({
parallel: true,
cache: true,
sourceMap: true
})
]
},
plugins: [
new CompressionPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
test: /\.(js|html|svg)$/,
threshold: 8192,
minRatio: 0.8
}),
new BrotliPlugin({
asset: "[path].br[query]",
test: /\.(js|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
]
});
.babelrc
{
"presets": ["#babel/preset-env", "#babel/preset-react"],
"plugins": [["#babel/transform-runtime"]]
}
THX for any help.

If you run the build script you will get the optimized code that you can deploy on a webserver. Running npx serve build would be an easy way to simulate a webserver with the bulid files. I guess the dist script does the same using the webpack dev server.
Text compression is something you would normally configure on the webserver or proxy that hosts your website. I guess these webpack plugins do the same thing but it's normally a one liner in a webserver or proxy and you don't have to interfere with the create-react-app standard config.
Text compression examples:
Express.js
Nginx

Here the solution:
I added server.js
const express = require("express");
const path = require("path");
const port = 8080;
const app = express();
app.get("*.js", (req, res, next) => {
req.url = req.url + ".br";
res.set("Content-Encoding", "br");
res.set("Content-Type", "application/javascript; charset=UTF-8");
next();
});
app.use(express.static("./dist"));
app.get("*", (req, res) => {
res.sendFile(path.resolve("./dist", "index.html"));
});
app.listen(port);
console.log("Server started");
Also changed my package.json
"scripts": {
"stard": "webpack-dev-server --config webpack.dev.js --open --progress --colors --hot",
"build": "webpack --config webpack.prod.js",
"prod": "node server.js"
},
had to add to my webpack.prod.js publicPath: "/"
...
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[hash].bundle.js",
publicPath: "/"
},
...

Related

why process.env doesn't work in app.js file but works in webpack.config.js?

why process.env doesn't work in app.js file but works in webpack.config.js? I have my own webpack that I created myself. It was not created with CRA
the version so it is correct to deduce? Or is there any way to get it out? Or is it better not to drive at all? since it seems to me that this is also not correct, since the env file should be in the exception and on the server, when loading, probably nothing will be known about the version ...
I want to output the version to the console so I can see the webpack version. But I can't do it even though I made the necessary settings. if I output data in webpack, then everything works correctly if I output data in a simple js file, erroneous errors. in general, I want to make the version visible on the server after the build, and I want to do it in this way, you can simply output console.log, but this seems like the wrong solution console.log('ver 1.74.2')
import React from "react";
import styles from "./styles.scss";
const onClickEvent = (e) => {
e.preventDefault();
alert("You Clicked Me!");
};
const App = () => {
console.log(process.env.TERM_PROGRAM_VERSION);
return (
<div className={styles.content}>
<div className={styles.label}>OWN WEBPACK</div>
<button className={styles.btn} onClick={onClickEvent}>
Click Me 😎
</button>
</div>
);
};
export default App;
const webpack = require("webpack");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const production = process.env.NODE_ENV === "production";
const Dotenv = require("dotenv-webpack");
console.log(process.env.TERM_PROGRAM_VERSION);
module.exports = {
entry: { myAppName: path.resolve(__dirname, "./src/index.js") },
output: {
path: path.resolve(__dirname, "./dist"),
filename: production ? "[name].[contenthash].js" : "[name].js",
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
{
test: /\.s(a|c)ss$/,
exclude: /node_modules/,
use: [
production ? MiniCssExtractPlugin.loader : "style-loader",
{
loader: "css-loader",
options: {
modules: true,
sourceMap: !production,
},
},
{
loader: "sass-loader",
options: {
sourceMap: !production,
},
},
],
},
],
},
resolve: {
extensions: ["*", ".js", ".jsx", ".scss"],
},
plugins: [
new CleanWebpackPlugin(),
new Dotenv(),
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
title: "Webpack & React",
template: "./src/index.html",
favicon: "./public/favicon.ico",
}),
new MiniCssExtractPlugin({
filename: production ? "[name].[contenthash].css" : "[name].css",
}),
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
"process.env.MY_ENV": JSON.stringify(process.env.MY_ENV),
}),
],
devServer: {
port: 3001,
hot: true,
},
mode: production ? "production" : "development",
};
"scripts": {
"start": "npm run build && node server/index.js",
"dev": "NODE_ENV=development test=$TERM_PROGRAM_VERSION webpack serve --config webpack.config.js",
"build": "NODE_ENV=production webpack --config webpack.config.js",
"build:dev": "NODE_ENV=development webpack --config webpack.config.js",
"build:watch": "NODE_ENV=development node ./node_modules/webpack/bin/webpack.js --config webpack.config.js --watch"
},
//server/index.js
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
if (process.env.NODE_ENV === 'development') {
console.log('in development.');
} else {
console.log('in production.');
}
/* App Config */
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, '../dist')));
/* Server Initialization */
app.get('/', (req, res) => res.sendFile('index.html'));
var port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Server initialized on: http://localhost:${port} // ${new Date()}`));
Change your env variable to REACT_APP_TERM_PROGRAM_VERSION then reload your app.
React env variables must start with REACT_APP_ prefix. See https://create-react-app.dev/docs/adding-custom-environment-variables/

How to discover if lazy loading is working or not

I am in middle of optimising my react app bundle. Current size is 1.4MB. Implemented Lazy loading in routers. While running the app at localhost, i can see lazy loading working well in Network tab of browser, I see first initial chunk loads and render's in the browser then rest of the 1.4MB comes. Problem comes when i create a production bundle and deploy it to server, there entire 1.4MB loads and then can see rendering.
Is there something missing during production bundle creation? How to check if lazy loading is working from server?
Webpack.config.js
const path = require('path');
const { resolve } = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
var CompressionPlugin = require('compression-webpack-plugin');
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
module.exports = {
devtool: 'cheap-module-source-map',
entry: './src/index.jsx',
resolve: {
fallback: { crypto: false },
extensions: ['.js', '.jsx', '.json', '.wasm'],
enforceExtension: false,
alias: {
process: resolve('node_modules/process')
}
},
devServer: {
historyApiFallback: true,
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
module: {
rules: [
{
test: /\.js$|jsx/,
loader: 'babel-loader',
exclude: /node_modules[/\\\\](?!(mesh-component-library|mesh-icon-library)[/\\\\]).*/
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
}
]
},
{
test: /\.sass$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'sass-loader'
}
]
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'images/[hash]-[name].[ext]'
}
}
]
}
]
},
plugins: [
new webpack.ProvidePlugin({ process: 'process/browser' }),
new HtmlWebpackPlugin({ template: './public/index.html' }),
new MiniCssExtractPlugin({ filename: 'styles.css' }),
new webpack.EnvironmentPlugin({
NODE_ENV: process.env.BABEL_ENV,
BABEL_ENV: process.env.NODE_ENV
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new CompressionPlugin({
algorithm: "gzip",
threshold: 10240,
minRatio: 0.8
})
]
};
Package.json
"scripts": {
"test": "jest --watchAll=false --coverage",
"testWithResults": "jest --json --outputFile=./testResults.json",
"start": "webpack-dev-server --mode development --config webpack.config.js --open --port 4000",
"build": "webpack --mode production --config webpack.config.js",
"eslint": "eslint src/**/*.js*"
},
Try configuring webpack SplitChunksPlugin to include all.
Add this section to your webpack config.
optimization: {
splitChunks: {
chunks: 'all',
}
}
I would suggest using the Webpack bundle analyzer, it has helped me differentiate between bundle sizes before and after implementing dynamic loading recently. The implementation is quite simple, they've mentioned it in the docs. It's easy to understand the differences in size as well as everything is visual. Hope this helps!

React App ignores NODE_ENV of the production mode

I'm fairly new to React and I've built a client side react app using webpack as module bundler and npm. It works smoothly in development with Webpack devServer. While in production, I've used express as a server. When running at localhost:8080, it displays fine but I get these warnings. I set up the NODE_ENV='production', but still same warnings.
Here is my production configuration file
production.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const webpack = require('webpack')
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const config ={
devtool: 'cheap-module-source-map',
entry: [
'react-hot-loader/patch',
'./client/main.js'
],
output: {
path: path.resolve(__dirname, 'build'),
filename: 'app.bundle.js',
publicPath:'/'
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
//query: { sourceMap: false },
options: {
importLoaders: 1,
}
},
{
loader: 'postcss-loader'
}
]
})
},
{
test: /\.jsx?$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpe?g|gif|svg|ico|\.woff$|\.ttf$|\.wav$|\.mp3$)$/i,
use: ['file-loader?name=img/[name].[ext]',
'image-webpack-loader']
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
loader: 'file-loader?name=fonts/[name].[ext]'
}
]
},
plugins: [
//index.html custom template
new HtmlWebpackPlugin({
title: 'Index',
template: './index.html'
}),
new webpack.EnvironmentPlugin(
{
'process.env':
{
NODE_ENV: JSON.stringify('production') }
}
),
//extract css files
new ExtractTextPlugin({filename:"styles.css",disable:false,allChunks:true}),
new UglifyJsPlugin({
sourceMap: false,
mangle: true,
beautify:false,
compress: {
warnings: false, // Suppress uglification warnings
pure_getters: true,
unsafe: true,
unsafe_comps: true,
screw_ie8: true
},
output: {
comments: false
}
})
]
};
module.exports=config
package.json
{
"name": "react-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"webpack": "webpack",
"dev": "webpack-dev-server --colors",
"prod": "npm run build && node deploy.js",
"build": "webpack --config production.config.js --progress --colors"
}
//dependencies are omitted
}
Try using this package: https://www.npmjs.com/package/cross-env
I believe there is a problem with setting NODE_ENV=production in windows command prompts which it solves.
Usage example:
Build script:
cross-env NODE_ENV=production webpack
webpack.config.js:
const webpack = require('webpack');
const env = process.env.NODE_ENV || 'production';
//...
plugins = [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(env)
}
})
]
//...
Hope that helps.

hot-reload angularjs with webpack

I use webpack with this configuration:
var webpack = require('webpack');
var CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
context: __dirname + '/app',
entry: {
app: './app.js',
vendor: ['angular', 'angular-route', 'angular-resource', 'angular-bootstrap']
},
output: {
path: __dirname + '/app',
filename: 'js/app.bundle.min.js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(/* chunkName= */"vendor", /* filename= */"js/vendor.bundle.min.js"),
new CopyWebpackPlugin([
{ from: "../node_modules/bootstrap/dist/fonts/", to:"fonts/"},
{ from: "../node_modules/bootstrap/dist/css/bootstrap.min.css", to:"css/"}
]),
new webpack.optimize.UglifyJsPlugin({
minimize: true,
sourceMap: false,
mangle: false
})
]
};
In script of my package.json i have:
scripts": {
"clean": "rimraf ../assets",
"prestart": "npm install",
"build": "webpack",
"start": "webpack-dev-server -d --hot --inline --content-base ./app"
},
It is possible reload application when i change everything in js file without stop and start server?
Check it out angular-hot-reloader. It is based on a few simple webpack loaders which are just adding a little piece of code to each reloadable file. The piece of code declares the file as hot-reloadable to webpack, and then does some hacky magic to make angular aware of the changes, recompiling the templates against the scope, and patching the controller’s prototype methods.
Usage:
const componentHotLoader = require.resolve('../loaders/component-loader');
const serviceHotLoader = require.resolve('../loaders/service-loader');
const jadeHotLoader = require.resolve('../loaders/jade-loader');
// add componentHotLoader and serviceLoader
(webpackConf.module.preLoaders = webpackConf.module.preLoaders || []).push(
{ test: /\.component\.js$/, loader: componentHotLoader, exclude: [/client\/lib/, /node_modules/, /\.spec\.js/] }
);
(webpackConf.module.preLoaders = webpackConf.module.preLoaders || []).push(
{ test: /\.service\.js$/, loader: serviceHotLoader, exclude: [/client\/lib/, /node_modules/, /\.spec\.js/] }
);
(webpackConf.module.postLoaders = webpackConf.module.postLoaders || []).push(
{ test: /\.html/, loader: jadeHotLoader }
);
You can use this example as a reference

Hot Loader not working

I am trying to use Hot Loader. I have installed Hot Loader and followed all the steps from the documentation http://gaearon.github.io/react-hot-loader/getstarted/. I then give "npm start" command to the terminal. But terminal throws an error. I am not able to see the changes I make in the component getting reflected in browser.
webpack.config.js
var path = require('path');
var webpack = require('webpack');
module.exports = {
devServer: {
inline: true,
contentBase: './src',
port: 3000
},
devtool: 'cheap-module-eval-source-map',
entry: [
'webpack-dev-server/client?http://localhost:3000', // WebpackDevServer host and port
'webpack/hot/only-dev-server', // "only" prevents reload on syntax errors
'./dev/js/index.js' // Your appʼs entry point
],
module: {
loaders: [
{
test: /\.js$/,
loaders: ['react-hot','babel'],
exclude: /node_modules/
},
{
test: /\.scss/,
loader: 'style-loader!css-loader!sass-loader'
}
]
},
output: {
path: 'src',
filename: 'js/bundle.min.js'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.OccurrenceOrderPlugin()
]
};
Script part of Package.json
"scripts": {
"dev": "webpack",
"start": "node server.js"
},
server.js
var webpack = require('webpack');
var webpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
var port = 3000;
var compiler = webpack(config);
new webpackDevServer(compiler, {
publicPath: config.output.publicPath,
hot:true,
stats:{colors:true}
}).listen(port, 'localhost', function (err, result) {
if(err){
console.log(err);
}
console.log('listening at localhost' + port);
});
Few quetions
1. Is my server.js file is fine.
2. Have I included everything correctly in scripts in package.json.
3. Is "npm start" a command to launch hot loader.
4. Even after getting error after "npm start", I can launch my application
in the browser by clicking the chrome icon. What is the reason.
5. What should I change in these files to get a working hot loader.

Resources