📜 ⬆️ ⬇️

Optimize frontend. Part 1. Why I don’t like the word treeshaking or where the webpack deceives you


We treat the technologies we use as shopping on the Yandex Market. We look at the specification, read the reviews and, if the project received a lot of stars on the githaba, it passes according to the specification, and also introduction is inexpensive, we have it we buy install. Such an approach sometimes beats the rake very hard on the head, and then you still have to figure out what is happening.


Prehistory


In an article by one of the authors of the rollup , two optimizations are considered, one is called dead code elimination , and the second is tree-shaking . The author shows that tree-shaking has a lot more code compression capabilities. And as proof, he gives several considerations about the recipes of the cake and the broken eggs. Oh, those metaphors!


This idea (about tree-shaking, not about cake and eggs) was picked up by the webpack development team and from version 2.0 it began to officially support.


Problem


I would not write if the technology on real projects brought at least some result. In practice, the dimensions of the final assembly either do not decrease at all or are reduced by the size of the statistical error.


Some, of course, guess about the trick and even write articles on Habr . But lovers to speculate about the benefits of tree-shaking over the dead code illumination in a webpack around do not get less, at least among conference visitors and among my colleagues.


How was this supposed to work?


The idea is as simple as a tank .


  1. The collector goes through the module tree and marks unused imports with special comments. Like this:
    ... /* unused harmony export square */ function square(x) { return x * x;} ... 
  2. The next step, UglifyJS, which by default is taped to the webpack with an electrical tape, besides all that we expect from it, mercilessly cuts out the code that is marked with these very comments.
  3. PROFIT!

When does it not work?


Suppose we have everything in the documentation. Two files index.js and module.js


 // index.js import {cube} from './module' console.log(cube(x)) 

 // module.js export function square(x) { return x * x; } export function cube(x) { return x * x * x; } 

If we now launch the webpack in optimization and minimization mode, then everything will work as expected. ( code )


 webpack --optimize-minimize index.js out.js 

But if only any, even the smallest export class with a babel-loader is added to the module file, write to it. The class will fall into the final assembly as a function that the babel spat out. ( code )


 //module.js export function square(x) { return x * x; } export function cube(x) { return x * x * x; } export class MyClass { print(){ console.log('find me'); } } 

How did it happen?


The thing is that UglifyJS is afraid to throw out something extra. It is understandable: let it be better for a couple of hundred bytes more, if only it would not break.


And so, imagine that UglifyJS receives the following code as input:


 /* unused harmony export MyClass */ var MyClass = function () { function MyClass() { babelHelpers.classCallCheck(this, MyClass); } MyClass.prototype.turn = function print() { console.log('find me'); }; return MyClass; }(); 

MyClass, after compiling babel, somehow got out of recognizing itself as a class. And in general, UglifyJS knows little about how the babel team sees the implementation of classes on ES5. So he succumbs, leaving this unknown unthinkable disgrace in your final assembly.
There is even a bug in the webpack repository, and the guys promise to fix everything in version 4 .
Rollup, by the way, not so long ago also worked only on examples with mathematics, but in recent versions the guys fixed a bug. ( broken example , working example ).


Morality


So, having bought a webpack, including for tree-shaking, I got a near-zero benefit in this direction. And the word tree-shaking now makes me nervous laugh, hiccups and uncontrollable sarcasm.


And how to be now?


To begin with, drop the webpack and use my fundamentally new assembler , which is devoid of both these flaws and many others. In addition to the classic assembly, he brews craft beer and prepares burgers on ciabatta.


Sorry, could not resist.


But seriously, there is a very simple way to fix the situation:


you need the webpack to add a special directive /*#__PURE__*/ , which would tell UglifyJS that this unknown monster as a function can be cut to itself.


Oh, these crutches.


And it looks something like this:


 /* unused harmony export MyClass */ var MyClass = /*#__PURE__*/ function () { function MyClass() { babelHelpers.classCallCheck(this, MyClass); } MyClass.prototype.turn = function print() { console.log('find me'); }; return MyClass; }(); 

Just a couple of iterations and we will invent static typing;)


Working example


By the way, the new version of babel 7 is already doing this. Unfortunately, it is still in beta, which seems to hint at the impossibility of use right now. But if you are brave and decisive, you can try to upgrade.


Looking ahead, I will say that there are several solutions that are working right now. I will tell about them in the following article. Perhaps someone will save their nerves and time. And we have to go to the conclusions.


findings


  1. Doubt everything. Check the information. Even this article. This practice will save you a lot of nerves and time.
  2. The javascript ecosystem sometimes allows failures at the interfaces of technologies. And here, because babel said nothing to UglifyJS via webpack about its ES5 class format, there was a misunderstanding. At the same time, they almost fixed it in babel, of which, of course, the guys from the webpack don't know and want to fix it in the next release.

PS Write in the comments if you, too, came across the tricks of marketers and chose fashionable technology instead of solving problems.


Optimize frontend. Part 2


')

Source: https://habr.com/ru/post/342686/


All Articles