I'm new to Webpack/Babel and trying to leverage it to improve the lighthouse performance of a front-end website that utilizes React.
I ran the lighthouse scan and found a recommendation to remove unused javascript with the main component of this being Babel's asyncToGenerator.js.
Looking at my webpack output, I see the following:
WARNING in entrypoint size limit: The following entrypoint(s) combined
asset size exceeds the recommended limit (244 KiB). This can impact
web performance. Entrypoints: main (543 KiB)
vendors-node_modules_babel_runtime_helpers_asyncToGenerator_js-node_modules_babel_runtime_hel-<hash>.css
vendors-node_modules_babel_runtime_helpers_asyncToGenerator_js-node_modules_babel_runtime_hel-<hash>.js
main.css
main.js
webpack.js:
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require( 'path' );
const WebpackPwaManifest = require('webpack-pwa-manifest');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const PreloadWebpackPlugin = require('preload-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
context: __dirname,
entry: [
// "core-js/modules/es.promise",
// "core-js/modules/es.array.iterator",
'./src/index.js',
],
output: {
path: path.resolve( __dirname, 'build' ),
filename: '[name].js',
publicPath: '/',
chunkFilename: '[id].[chunkhash].js'
},
optimization: {
chunkIds: "named",
splitChunks: {
chunks: 'all',
},
},
devServer: {
historyApiFallback: true
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env'],
cacheDirectory: true
}
}
},
{
test: /\.(png|svg|jpg|jpeg|gif|ico)$/,
exclude: /node_modules/,
use: ['file-loader?name=[name].[ext]'] // ?name=[name].[ext] is only necessary to preserve the original file name
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
],
},
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve( __dirname, 'public/index.html' ),
filename: 'index.html',
favicon: 'public/favicon.ico',
title: 'Title'
}),
new PreloadWebpackPlugin({
rel: 'preload',
include: 'initial'
//include: 'initial',//'asyncChunks',//'initial',//'allChunks',
}),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name]-[id].css',
}),
new WebpackPwaManifest({
short_name: "Name",
name: "Name",
description: 'desc',
icons: [
{
src: path.resolve('public/favicon.ico'),
sizes: [64, 32, 24, 16]
},
],
start_url: "https://<url>.com/",
display: "browser",
theme_color: "#66fcf1",
background_color: "#1f2833"
}),
new CopyPlugin({
patterns: [
{ from: './src/serviceWorker.js', to: './serviceWorker.js' },
{ from: './public/offline.html', to: './offline.html' },
{ from: './public/manifest.json', to: './manifest.json' },
{ from: './public/manifest.json', to: './asset-manifest.json' },
{ from: './public/logo-png-192.png', to: './logo-png-192.png' },
{ from: './public/logo-png-512.png', to: './logo-png-512.png' },
{ from: './public/maskable_icon.png', to: './maskable_icon.png' },
],
}),
]
};
.babelrc
{
"presets": [
[
"#babel/preset-env", {
"modules": "auto",
"targets": "> 0.25%, not dead"
}
],
"#babel/preset-react"
],
"plugins": [
"#babel/plugin-proposal-class-properties",
[
"#babel/plugin-transform-runtime",
{
"regenerator": true
}
],
"#babel/plugin-syntax-dynamic-import"
]
}
What can I do to only include the asyncToGenerator when needed/only include what is needed?
The page does read from cloudfront and uses lazy loading.
Thoughts?
Related
I create a BLOB file and write JavaScipt code there, then create a URL and import the module from it.
const myJSFile = new Blob( [ 'export default true;' ], { type: 'application/javascript' } );
const myJSURL = URL.createObjectURL( myJSFile );
import( myJSURL ).then(async ( module ) => {
console.log( module.default );
});
This works great in the browser console. However, I am having a problem when building a project using Webpack.
I suspect the problem is with WebPack or Babel configuration.
Webpack common config:
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
// Where webpack looks to start building the bundle
entry: [
'core-js/modules/es6.promise',
'core-js/modules/es6.array.iterator',
'./src/main.js',
],
target: 'web',
// Where webpack outputs the assets and bundles
output: {
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: '[name].[contenthash].[ext]',
filename: '[name].[contenthash].bundle.js',
chunkFilename: '[id].[chunkhash].bundle.js',
// publicPath: '/',
},
// Determine how modules within the project are treated
module: {
rules: [
{
test: /\.(gif|png|jpe?g)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/images/'
}
}
]
},
// JavaScript: Use Babel to transpile JavaScript files
{ test: /\.js$/, use: ['babel-loader'] },
// Images: Copy image files to build folder
{ test: /\.(?:ico|gif|png|jpg|jpeg)$/i, type: 'asset/resource' },
// Fonts and SVGs: Inline files
{ test: /\.(woff(2)?|eot|ttf|otf|svg|)$/, type: 'asset/inline' },
],
},
// Customize the webpack build process
plugins: [
// Generates an HTML file from a template
new HtmlWebpackPlugin({
// template: path.resolve(__dirname, 'src/index.html'), // шаблон
template: 'src/index.html',
// filename: 'index.html', // название выходного файла
// inject: false, // true, 'head'
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
},
// chunks: 'all',
// excludeChunks: [],
}),
new CopyWebpackPlugin(
{
patterns: [
{ from: 'src/assets', to: 'assets' },
// { from: 'src/components', to: 'components' },
]
}
),
],
resolve: {
// modules: [path.resolve(__dirname, 'src'), 'node_modules'],
extensions: ['.js', '.jsx', '.json', '.ts'],
alias: {
'#': [
path.resolve(__dirname, 'src'),
'./src/main.js'
],
},
},
}
Babel config
module.exports = {
presets: [
[
'#babel/preset-env',
{
targets: {
esmodules: true,
},
},
],
],
plugins: [
'#babel/plugin-proposal-class-properties',
'#babel/plugin-transform-runtime',
"#babel/plugin-syntax-dynamic-import"
]
};
I've used require-from-string before, which internally uses the native Module module to achieve this.
Speaking generally, Blobs don't really interoperate well between browser and node land, and modules are also not treated identically.. so it's not a surprise that blob+module has problems. :)
Why can't I embed images in webpack with HTML img tags?
Below is the content of webpack.config.js.
// webpack.config.js
const {
CleanWebpackPlugin,
} = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = [{
mode: 'development',
entry: {
'px.bundle': ['./src/index.js'],
},
output: {
path: path.resolve(__dirname,'dist'),
filename: '[name].js'
},
module:{
rules:[
{
test:/\.css$/,
use:[
'style-loader',
'css-loader'
]
},
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
},
],
}
]
},
devServer: {
static : {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 9000
},
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ["**/*", "!assets/**/*", "!resources/**/*"],
}),
new MiniCssExtractPlugin({
filename: 'style.css',
}),
new HtmlWebpackPlugin({
title: 'Project Demo',
minify: {
collapseWhitespace: true
},
hash: true,
template: './src/index.html'
}),
],
performance: {
hints: false,
maxEntrypointSize: 512000,
maxAssetSize: 512000,
},
}, {
mode: 'product',
entry: {
'px.bundle': ['./src/index.js'],
},
output: {
path: path.resolve(__dirname,'dist'),
filename: '[name].js',
},
module:{
rules:[
{
test:/\.css$/,
use:['style-loader','css-loader']
}
]
},
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ["**/*", "!assets/**"],
cleanOnceBeforeBuildPatterns: ["**/*", "!resources/**"],
cleanAfterEveryBuildPatterns: ['dist'],
}),
new MiniCssExtractPlugin({
filename: 'style.css',
}),
new HtmlWebpackPlugin({
title: 'Project Demo',
minify: {
collapseWhitespace: true
},
hash: true,
template: './src/index.html'
}),
],
performance: {
hints: false,
maxEntrypointSize: 512000,
maxAssetSize: 512000,
},
},
];
And below is the code including the contents of index.html.
<img class="header__logo" src="img/ico_logo.png" alt="company logo" />
If enter the npm run dev command like this, the contents of img/ico_logo.png are not output properly. Of course, I checked that the file is properly located in the path.
Change the above code to the following,
<img class="header__logo" alt="company logo" />
If I include the following in my css
.header__logo{
content:url('../img/ico_logo.png')
}
If enter the npm run dev command, I can see that it works properly.
If specify src as an img tag in HTML, webpack cannot include the image.
I don't know if the webpack setting is wrong or if webpack works normally only through the url in css.
I'm wondering if for some reason webpack is not embedding the logo image properly, and how can I fix it?
you can try with this
{
test: /\.(jpg|jpeg|png|gif|pdf|ico)$/,
use: [
{
loader: "file-loader",
options: {
name: "images/[name].[ext]",
},
},
],
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {
minimize: true,
},
},
],
},
I think the reason is that webpack can't read HTML, although you had used HtmlWebpackPlugin. It just helps you to extract the HTML to a file, but it looks like it doesn't read the content.
I had the same problem, but I solved it by adding use html-loader.
I increase a new object into rules.
,{
test: /\.html$/i,
loader: 'html-loader'
}
And it works.
And also, the file-loader has been replaced by Asset Modules in webpack 5.
Hope this could help you, sincerely.🙂
The Webpack Bulid runs pretty slow, project background is a community portal developed in Vue.js...
Can anybody tell if there is any potential for improvement and if so, what?
I wonder if the time for the build process of 37401ms can still be changed by changing the codebase?
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const glob = require('glob');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const autoprefixer = require('autoprefixer');
const statsSettings = {
all: false,
modules: true,
maxModules: 0,
errors: true,
warnings: false,
moduleTrace: true,
errorDetails: true,
timings: true,
performance: true,
builtAt: true,
};
const {
rootDir,
srcDir,
assetsDir,
stylesDir,
buildDir,
sitepackageDir,
publicPath,
} = require('./config');
const chunks = glob.sync(path.join(rootDir, srcDir, 'pages/**/index.js'))
.reduce((obj, file) => {
const name = path.basename(path.dirname(file));
return {
...obj,
[name]: file,
};
}, {});
module.exports = env => {
return {
mode: env.production ? 'production' : 'development',
context: path.join(rootDir, srcDir),
entry: chunks,
output: {
path: path.join(rootDir, buildDir),
filename: '[name].js',
publicPath: env.production ? publicPath : '',
},
devtool: env.production ? false : 'cheap-module-eval-source-map',
devServer: {
contentBase: path.join(rootDir, buildDir),
inline: true,
proxy: {
'/api/v0': 'http://localhost:4000',
},
},
watchOptions: {
ignored: env.watch ? 'node_modules' : '',
aggregateTimeout: 300,
},
stats: env.watch ? statsSettings : 'normal',
module: {
rules: [
{
test: /\.vue$/,
include: [
path.join(rootDir, srcDir),
require.resolve('bootstrap-vue'),
],
loader: 'vue-loader',
},
{
test: /\.js$/,
include: [
path.join(rootDir, srcDir),
require.resolve('bootstrap-vue'),
],
loader: 'babel-loader',
},
{
test: /\.(css|scss)$/,
use: [
env.production ? MiniCssExtractPlugin.loader : 'vue-style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [
autoprefixer({
browsers: ['>1%', 'last 2 versions', 'not ie < 11'],
}),
],
},
},
{
loader: 'sass-loader',
options: {
includePaths: [
path.join(rootDir, srcDir, stylesDir),
],
},
},
],
},
{
test: /\.(jpg|jpeg|png|gif|webp|svg|eot|otf|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$/,
loader: 'file-loader',
options: {
name: `${assetsDir}/_processed_/[name].[hash:4].[ext]`,
},
},
],
},
optimization: {
runtimeChunk: {
name: '_runtime',
},
splitChunks: {
cacheGroups: {
// we need to figure out if it's worth having a common chunk
// or if each entry chunk should be somewhat self-contained
common: {
chunks: 'initial',
name: '_common',
minChunks: 2,
minSize: 0,
},
vendor: {
test: /node_modules/,
chunks: 'initial',
name: '_vendor',
enforce: true,
},
},
},
},
resolve: {
extensions: ['.js', '.json', '.vue'],
alias: {
'#app': path.join(rootDir, srcDir),
// although we're using single file components that can be pre-compiled,
// we want to dynamically mounting them in the DOM html.
// this is way we need to alias 'vue' to use the runtime + compiler build here.
// see: https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only
vue$: 'vue/dist/vue.esm.js',
},
},
plugins: [
new webpack.DefinePlugin({
PAGES: JSON.stringify(Object.keys(chunks)),
}),
new VueLoaderPlugin(),
...plugHtmlTemplates(),
...plugExtractCss(env),
...plugCopyAssets(),
],
};
};
function plugHtmlTemplates () {
return glob.sync(path.join(rootDir, srcDir, 'pages/**/template.html'))
.map(template => {
const name = path.basename(path.dirname(template));
return {
template,
filename: `${name}.html`,
chunks: ['_runtime', '_vendor', '_styles', '_common', name],
};
})
.map(htmlConfig => new HtmlWebpackPlugin(htmlConfig));
}
function plugExtractCss (env) {
if (!env.production) return [];
return [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name].css',
}),
];
}
function plugCopyAssets () {
const assetsSrcPath = path.join(rootDir, srcDir, assetsDir);
if (!fs.existsSync(assetsSrcPath)) return [];
return [
new CopyWebpackPlugin([
{ from: assetsSrcPath, to: path.join(rootDir, buildDir, path.basename(assetsDir)) },
// this is required for the icons to be selectable in the backend
{ from: path.join(assetsSrcPath, 'icons/'), to: path.join(rootDir, sitepackageDir, 'Resources/Public/Icons/') },
// this is required for avatars to be available; we must not check them in in fileadmin since they would
// prevent having the dir linked by Deployer during a deployment
{ from: path.join(rootDir, '../packages/users/Resources/Private/Images/Avatars/'), to: path.join(rootDir, buildDir, 'static/avatars/') },
]),
];
}
The question is whether you can improve the performance by using different plugins or summarizing steps...
I have a similar configuration. My configuration built ~33 seconds. I added a cache-loader package and decrease build time to ~17 seconds.
npm install --save-dev cache-loader
config:
rules: [
//rules here
{
test: /\.vue$/,
use: [
{
loader: 'cache-loader',
options: {}
},
{
loader: 'vue-loader',
options: vueLoaderConfig
}
],
},
{
test: /\.js$/,
use: [
{
loader: 'cache-loader',
options: {}
},
{
loader: 'babel-loader'
}
],
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
}
]
I am trying to build the react app with minification using uglifyjs-webpack-plugin But it's failing in production mode due to UglifyJS.
I am getting the following error.
ERROR in bundle.js from UglifyJs
Unexpected token: keyword «const» [./src/utils/Constants.js:1,0][bundle.js:228154,0]
Below is my webpack config may be I am missing some babel plugin. Any help would be appreciated.
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const bourbon = require('node-bourbon').includePaths;
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HOST = process.env.HOST || 'localhost';
const PORT = process.env.PORT || 3000;
const PROXY = `http://${HOST}:${PORT}`;
const config = {
entry: ["core-js/stable", "regenerator-runtime/runtime", "./src/index.js"],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
publicPath: '/'
},
module: {
rules: [{
test: /.(jsx|js)?$/,
exclude: [
path.resolve(__dirname, 'node_modules'),
],
loader: 'babel-loader',
options: {
"presets": [
[
"#babel/preset-env",
{
"targets": {
"node": "current"
}
}
],
"#babel/preset-react"
],
"plugins": [
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-syntax-import-meta",
"#babel/plugin-proposal-class-properties",
["#babel/plugin-proposal-decorators", { "legacy": true }],
["#babel/plugin-proposal-class-properties", { "loose": true }],
"#babel/plugin-proposal-function-sent",
"#babel/plugin-proposal-export-namespace-from",
"#babel/plugin-proposal-numeric-separator",
"#babel/plugin-proposal-throw-expressions",
"#babel/plugin-proposal-export-default-from",
"#babel/plugin-proposal-logical-assignment-operators",
"#babel/plugin-proposal-optional-chaining",
]
}
},
{
test: /.(css|scss|sass)$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [
autoprefixer(),
]
}
}, 'sass-loader?includePaths[]=' + bourbon,
]
},
{
test: /\.(png|jpg|jpeg|gif|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: 'url-loader?limit=10000&name=img/[hash].[ext]'
},
{
test: /\.(ttf|ttc|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: 'url-loader?limit=10000&name=fonts/[hash].[ext]'
}
]
},
resolve: {
extensions: ['.json', '.js', '.jsx', '.css'],
},
devtool: 'source-map',
devServer: {
historyApiFallback: true,
host: HOST,
port: PORT,
},
plugins: [
new MiniCssExtractPlugin({
filename: './css/style.css',
chunkFilename: "[id].css"
}),
new webpack.LoaderOptionsPlugin({
options: {
postcss: [
autoprefixer(),
]
}
}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
}),
new BrowserSyncPlugin({
// browse to http://localhost:3000/ during development,
// ./public directory is being served
host: HOST,
port: PORT,
proxy: PROXY
}),
new CopyWebpackPlugin([{
from: './*.html',
to: './',
context: 'public/'
},
{
from: './*.ico',
to: './',
context: 'public/'
},
{
from: './img/**/*',
to: './',
context: 'public/'
},
{
from: './fonts/**/*',
to: './',
context: 'public/'
},
{
from: './js/**/*',
to: './',
context: 'public/'
}
], {
copyUnmodified: true
}),
],
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({})
]
},
performance: {
hints: false
} // Bundle warnings
};
module.exports = config;
I want to bundle a couple of html files and add one css files into all of them. But my webpack configuration add only css and js bundled files to index.html
My files looks like this:
I want to add this main-hash.js and main-hash.css to all HTML files: about.html, index.html, contact.html
Also when I exclude index.html (comment below) from file-loader my index.html don't recognize paths for public folder /images. Any ideas ??
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/main.js',
output: {
filename: '[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[hash:8].css'
}),
new CleanWebpackPlugin()
],
module: {
rules: [
{
test: /.(js|jsx)$/,
include: [path.resolve(__dirname, 'src')],
loader: 'babel-loader',
options: {
plugins: ['syntax-dynamic-import'],
presets: [
[
'#babel/preset-env',
{
modules: false
}
]
]
},
exclude: '/node_modules/'
},
{
test: /\.(png|jpeg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
publicPath: './src/images',
outputPath: './assets',
name: '[name].[ext]'
}
}
]
},
{
test: /\.(html)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].html'
}
},
{ loader: 'extract-loader' },
{
loader: 'html-loader',
options: {
attrs: ['img:src']
}
}
],
// HERE
exclude: path.resolve(__dirname, './src/index.html')
},
{
test: /\.sass$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
'css-loader',
'sass-loader'
]
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
priority: -10,
test: /[\\/]node_modules[\\/]/
}
},
chunks: 'async',
minChunks: 1,
minSize: 30000,
name: true
}
},
devServer: {
open: true
}
};
You can add multiple instances of the HtmlWebpackPlugin, one for each of your html files:
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index.html'
}),
new HtmlWebpackPlugin({
filename: 'about.html',
template: './src/about.html'
}),
new HtmlWebpackPlugin({
filename: 'contact.html',
template: './src/contact.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[hash:8].css'
}),
new CleanWebpackPlugin()
],
This should inject the JS and CSS files into each html file.