4 tips for optimizing a webpack application
Hello!
During my work with the webpack, I have a couple of interesting tips that will help you to prepare a perfectly optimized application. Let's get started!

Usually, developers use @ babel / preset-env to convert all modern syntax to ES5.
With this preset, the asynchronous function transformation mappings look like this:
Initial asynchronous function -> Generator -> Function using regenerator-runtime
With fast-async, the pipeline is simplified to:
Initial asynchronous function -> Function using promises
Due to this, now we do not have a regenerator-runtime on the client and no extra transformation wrappers.
To bring fast-async into your project, you need:
1. Install it
2. Update Babel's config
I have this optimization reduced the size of js files by 3.2%. Trifle, but nice :)
Without a special setting, @ babel / preset-env attempts to generate a code as close as possible to the specification.
But, most likely, your code is not so bad and does not use all possible extreme cases of the ES6 + specification. Then you can remove all the extra overhead by including loose transformations for the preset env:
An example of how this works can be found here .
In my project, this reduced the size of the bundle by 3.8%.
The default settings for minifiers contain only those transformations that the programmer cannot break. But we love to give ourselves problems?
Try reading the settings of the minifiers js and your minifiers css (I use cssnano ).
Having studied the docks, I made this config:
As a result, the size of js files decreased by 1.5%, and css - by 2%.
Maybe you can do better?
UPD 11.01.2019 : UglifyJsPlugin is outdated, webpack now uses TerserWebpackPlugin . Use it.
The gsap developers have a great library for creating animations. But due to the fact that it originates from 2008, there are still some peculiarities in it.
Namely , this one . Thanks to it, TweenMax draws 5 plug-ins and easePack, which are completely optional.
I noticed three extra plugins in my home and sawed them out using the null-loader :
And 106 kb turn into 86. Ta-da!
Null-loader can still be used to remove unnecessary polyfill, which the authors of the libraries carefully planted to us.
During my work with the webpack, I have a couple of interesting tips that will help you to prepare a perfectly optimized application. Let's get started!

1. Use fast-async instead of regenerator-runtime
Usually, developers use @ babel / preset-env to convert all modern syntax to ES5.
With this preset, the asynchronous function transformation mappings look like this:
Initial asynchronous function -> Generator -> Function using regenerator-runtime
Example
1. Initial asynchronous function
2. Generator
3. Function using the regenerator runtime
const test = async () => {
await fetch('/test-api/', { method: 'GET' });
}
2. Generator
function_asyncToGenerator(fn) { returnfunction () { var gen = fn.apply(this, arguments); returnnewPromise(function (resolve, reject) { functionstep(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { returnPromise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
const test = (() => {
var _ref = _asyncToGenerator(function* () {
yield fetch('/test-api/', { method: 'GET' });
});
returnfunctiontest() {
return _ref.apply(this, arguments);
};
})();
3. Function using the regenerator runtime
'use strict';
function_asyncToGenerator(fn) { returnfunction () { var gen = fn.apply(this, arguments); returnnewPromise(function (resolve, reject) { functionstep(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { returnPromise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
var test = function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function_callee() {
return regeneratorRuntime.wrap(function_callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case0:
_context.next = 2;
return fetch('/test-api/', { method: 'GET' });
case2:
case'end':
return _context.stop();
}
}
}, _callee, undefined);
}));
returnfunctiontest() {
return _ref.apply(this, arguments);
};
}();
With fast-async, the pipeline is simplified to:
Initial asynchronous function -> Function using promises
Example
1. Initial asynchronous function
2. Function using promises
const test = async () => {
await fetch('/test-api/', { method: 'GET' });
}
2. Function using promises
var test = functiontest() {
returnnewPromise(function ($return, $error) {
returnPromise.resolve(fetch('/test-api/', {
method: 'GET'
})).then(function ($await_1) {
try {
return $return();
} catch ($boundEx) {
return $error($boundEx);
}
}, $error);
});
};
Due to this, now we do not have a regenerator-runtime on the client and no extra transformation wrappers.
To bring fast-async into your project, you need:
1. Install it
npm i fast-async
2. Update Babel's config
// .babelrc.jsmodule.exports = {
"presets": [
["@babel/preset-env", {
/* ... */"exclude": ["transform-async-to-generator", "transform-regenerator"]
}]
],
/* ... */"plugins": [
["module:fast-async", { "spec": true }],
/* ... */
]
}
I have this optimization reduced the size of js files by 3.2%. Trifle, but nice :)
2. Use loose transformation
Without a special setting, @ babel / preset-env attempts to generate a code as close as possible to the specification.
But, most likely, your code is not so bad and does not use all possible extreme cases of the ES6 + specification. Then you can remove all the extra overhead by including loose transformations for the preset env:
// .babelrc.jsmodule.exports = {
"presets": [
["@babel/preset-env", {
/* ... */"loose": true,
}]
],
/* ... */
}
An example of how this works can be found here .
In my project, this reduced the size of the bundle by 3.8%.
3. Configure the minification of js and css by hands
The default settings for minifiers contain only those transformations that the programmer cannot break. But we love to give ourselves problems?
Try reading the settings of the minifiers js and your minifiers css (I use cssnano ).
Having studied the docks, I made this config:
// webpack.config.jsconst webpackConfig = {
/* ... */
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
unsafe: true,
inline: true,
passes: 2,
keep_fargs: false,
},
output: {
beautify: false,
},
mangle: true,
},
}),
new OptimizeCSSPlugin({
cssProcessorOptions: {
"preset": "advanced",
"safe": true,
"map": { "inline": false },
},
}),
],
},
};
/* ... */
As a result, the size of js files decreased by 1.5%, and css - by 2%.
Maybe you can do better?
UPD 11.01.2019 : UglifyJsPlugin is outdated, webpack now uses TerserWebpackPlugin . Use it.
4. Use null-loader to remove unwanted dependencies.
The gsap developers have a great library for creating animations. But due to the fact that it originates from 2008, there are still some peculiarities in it.
Namely , this one . Thanks to it, TweenMax draws 5 plug-ins and easePack, which are completely optional.
I noticed three extra plugins in my home and sawed them out using the null-loader :
// webpack.config.jsconst ignoredGSAPFiles = ['BezierPlugin', 'DirectionalRotationPlugin', 'RoundPropsPlugin'];
const webpackConfig = {
/* ... */module: {
rules: [
/* ... */
{
test: /\.js$/,
include: ignoredGSAPFiles.map(fileName => resolve('node_modules/gsap/' + fileName)),
loader: 'null-loader',
},
]
},
};
/* ... */
And 106 kb turn into 86. Ta-da!
Null-loader can still be used to remove unnecessary polyfill, which the authors of the libraries carefully planted to us.