Recently I upgrade my project from webpack1 to webpack3, one of the main highlight feature for me is Tree Shaking. Depnding on the static structure of ES6 module system, webpack is now able to get rid of unused modules from your build.
That sounds very cool, but after I finished my upgrade and rebuilt my project, the decrease of the file sizes did not reflect my expectation.
Why? Did I not use it right, or is it actually not working?
So then I created this test project which is used to test what impact will have on your code with webpack’s new tree shaking feature and what configuration needs to be made to enable the feature.
Test Example Code
I created two independent directories for test, one is for webpack1 the other is for webpack3.
In both directory, I have test code using lodash as well as my own code.
The examples are:
- partial import my own module:
import { a } from './lib
- import whole lodash:
import lodash from 'lodash
- import named lodash module:
import { uniq } from 'lodash
- import single lodash module:
import uniq from 'lodash/uniq
Notice for all lodash example, we only use the uniq
function.
Results
Check out the results for webpack1 and webpack2:
Webpack1
Asset Size Chunks Chunk Names
lodashImportAllTest.js 71.8 kB 0 [emitted] lodashImportAllTest
lodashNamedImportTest.js 71.8 kB 1 [emitted] lodashNamedImportTest
lodashSingleModuleImportTest.js 7.6 kB 2 [emitted] lodashSingleModuleImportTest
particalImportTest.js 472 bytes 3 [emitted] particalImportTest
+ 61 hidden modules
The dist file for partialImportTest.js
(the unused module b
is added in the dist file as well):
!function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={exports:{},id:o,loaded:!1};return t[o].call(r.exports,r,r.exports,e),r.loaded=!0,r.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}({0:function(t,e,n){"use strict";var o=n(57);(0,o.a)()},57:function(t,e){"use strict";function n(){return console.log("I am function a!")}function o(){return console.log("I am function b!")}Object.defineProperty(e,"__esModule",{value:!0}),e.a=n,e.b=o,e.default={a:n,b:o}}});
Webpack2
Asset Size Chunks Chunk Names
lodashSingleModuleImportTest.js 7.94 kB 0 [emitted] lodashSingleModuleImportTest
lodashNamedImportTest.js 72.5 kB 1 [emitted] lodashNamedImportTest
lodashImportAllTest.js 72.5 kB 2 [emitted] lodashImportAllTest
particalImportTest.js 647 bytes 3 [emitted] particalImportTest
The dist file for partialImportTest.js
(the unused module b
is not added in the dist file):
!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=60)}({60:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n(61).a()},61:function(e,t,n){"use strict";function r(){return console.log("I am function a!")}t.a=r}});
Result Conclusion
- Tree shaking does not work with
lodash
(lodash-es
as well, I have tried, and the file size is twice bigger) - Tree shaking works for my own code, webpack only imports what is used
- Webpack1 seems to generate smaller file.
Configuration Needed For Tree Shaking
The documentation for Tree Shaking is actually not complete, and I won’t blame you if you can’t make tree shaking work by following its documentation.
So for the tree shaking to work, there’s actually two dependencies:
- disable babel-loader’s es6 module transformation, let webpack take over it (the new ability for webpack is that it now understands es6 module)
- enable uglifyJS compression. Actually UglifyJS is the one which gets rid of unused code.
To disable your babel-loader’s es6 module transformation, update your options for babel-loader like below:
{
loader: 'babel-loader',
options: {
presets: [['es2015', { modules: false }], ...],
},
},
And add uglifyJS:
plugins:[
new webpack.optimize.UglifyJsPlugin(),
],
You will be ready to shake trees!
Final Conclusion
As soon as Tree Shaking does not work effectively with libraries like lodash
, the major benefits we can get may actually from downsizing our own code by getting rid of unused dead code.
However, for most project caring very much about its performance, they might already have been using tools like ESLint – Pluggable JavaScript linter to help them avoid bad practices during their development.
So I doubt how much impact will Tree Shaking have on serious projects, which is why I think you should lower your expectation, it is not a cure for your file size related performance issues, though it is good to have this extra feature.
Instead of relying on tree shaking, following best practices like alway import modules you actually use is a better way to keep your dist file small.
Do:
import uniq from 'lodash/uniq';
import { funA } from './myModule';
Don’t do:
import lodash from 'lodash';
import myModulefrom './myModule';
lodash.uniq(...);
myModule.funA(...);