Slide 1

Slide 1 text

СЖИМАЕМ JAVASCRIPT ПО- СЖИМАЕМ JAVASCRIPT ПО- ВЗРОСЛОМУ ВЗРОСЛОМУ Алексей Золотых 1

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

3

Slide 4

Slide 4 text

СЖИМАЕМ JAVASCRIPT ПО- СЖИМАЕМ JAVASCRIPT ПО- ВЗРОСЛОМУ ВЗРОСЛОМУ 4

Slide 5

Slide 5 text

– ЗАЧЕМ? – ЗАЧЕМ? – БЫСТРОДЕЙСТВИЕ! – БЫСТРОДЕЙСТВИЕ! 5

Slide 6

Slide 6 text

ЭКОНОМИЯ ЭКОНОМИЯ 6

Slide 7

Slide 7 text

Экономия Время на сеть Процессор Память 7

Slide 8

Slide 8 text

8

Slide 9

Slide 9 text

Пример 1 9

Slide 10

Slide 10 text

5mb 10

Slide 11

Slide 11 text

Network — 100ms 11

Slide 12

Slide 12 text

Parse — 300ms 12

Slide 13

Slide 13 text

Evaluate — 500ms 13

Slide 14

Slide 14 text

Пример 2 14

Slide 15

Slide 15 text

class Point { constructor() { ... this.x = x; ... } 15

Slide 16

Slide 16 text

Babel function _instanceof(left, right){...} function _classCallCheck(instance, Const function _defineProperties(target, props function _createClass(Constructor, proto 16

Slide 17

Slide 17 text

17

Slide 18

Slide 18 text

КАК? КАК? Потоковое сжатие Сжатие на основе AST 18

Slide 19

Slide 19 text

ПОТОКОВОЕ СЖАТИЕ ПОТОКОВОЕ СЖАТИЕ на уровне сервера 19

Slide 20

Slide 20 text

npx create-react-app my-app 20

Slide 21

Slide 21 text

Без кэша + Slow 3G 21

Slide 22

Slide 22 text

Размер 120k Время всего запроса 4 сек 22

Slide 23

Slide 23 text

Ожидание 2 сек Загрузка 2 сек 23

Slide 24

Slide 24 text

Сжатие в потоке gzip1— 50–90% gzip9 — 15–45% от gzip1 zopfli — 4–7% от gzip9 brotli11 — 8–65% от zopfli 24

Slide 25

Slide 25 text

Размер Время Было 120 4 Стало 40 2,7 25

Slide 26

Slide 26 text

30% БЫСТРЕЕ 30% БЫСТРЕЕ ЗАГРУЗКА СТРАНИЦЫ ЗАГРУЗКА СТРАНИЦЫ * * * для медленных сетей и быстрых устройств 26

Slide 27

Slide 27 text

27

Slide 28

Slide 28 text

28

Slide 29

Slide 29 text

... gzip on; gzip_static on; gzip_vary on; gzip_types ... brotli on; brotli_comp_level 4; brotli_static on; brotli types 29

Slide 30

Slide 30 text

ОПЫТ LINKEDIN ОПЫТ LINKEDIN http://bit.do/brotli 30

Slide 31

Slide 31 text

GIST С ОПИСАНИЕМ GIST С ОПИСАНИЕМ http://bit.do/jsgzip 31

Slide 32

Slide 32 text

Итого: Просто Для сети Не работает для маленьких файлов Почти всегда стоит включить 32

Slide 33

Slide 33 text

«Для js файлов на фоне космического коэффициента сжатия brotli считаю ненужным минификацию» 33

Slide 34

Slide 34 text

</head> <body> </body> 34

Slide 35

Slide 35 text

35

Slide 36

Slide 36 text

lodash.js По сети В браузере 87k 527k 36

Slide 37

Slide 37 text

Можем сжать? 37

Slide 38

Slide 38 text

UglifyJS используется в lodash 38

Slide 39

Slide 39 text

uglifyjs lodash.js > lodash.min.js 39

Slide 40

Slide 40 text

По сети В браузере 35k 144k 40

Slide 41

Slide 41 text

Убрали пробелы Убрали комменты 41

Slide 42

Slide 42 text

Протянули константы (function() { const a = 1; const b = 2; console.log(a + b); }()); console.log(3); 42

Slide 43

Slide 43 text

if(1 * 5){ console.log(1) } console.log(1); 43

Slide 44

Slide 44 text

arrows, booleans, collapse_vars, comparisons, computed_props, conditionals, dead_code, drop_console, drop_debugger, ecma, evaluate, expression, global_defs, hoist_funs, hoist_props, hoist_vars, ie8, if_return, inline, join_vars, keep_classnames, keep_fargs, keep_fnames, keep_infinity, loops, negate_iife, passes, properties, pure_getters, pure_funcs, reduce_funcs, reduce_vars, sequences, side_effects, switches, top_retain,

Slide 45

Slide 45 text

toplevel, typeofs, unsafe, unsafe_arrows, unsafe_comps, unsafe_Function, unsafe_math, unsafe_methods, unsafe_proto, unsafe_regexp, unsafe_undefined, unused, warnings, 44

Slide 46

Slide 46 text

Можем ещё? 45

Slide 47

Slide 47 text

uglifyjs lodash.js --mangle > lodash.min 46

Slide 48

Slide 48 text

* кроме функций верхнего уровня function lognLongLongName(mySuperArgs){ return mySuperArgs[0]; } function n(i){ return i[0]; } 47

Slide 49

Slide 49 text

По сети В браузере 23k 72k 48

Slide 50

Slide 50 text

Как толстеет бандл? 49

Slide 51

Slide 51 text

от 23к до 576к import * as _ from 'lodash' if (_.has([1, 2, 3], 2)) { console.log("debug"); } 50

Slide 52

Slide 52 text

от 3к до 10к import * as _ from 'lodash/has' if (_.has([1, 2, 3], 2)) { console.log("debug"); } 51

Slide 53

Slide 53 text

IMPORT COST IMPORT COST JetBrains VSCode bit.do/jcost bit.do/vcost 52

Slide 54

Slide 54 text

53

Slide 55

Slide 55 text

Как это скомпилит UglifyJS? import * as _ from 'lodash/has' if (_.has([1, 2, 3], 2)) { console.log("debug"); } 54

Slide 56

Slide 56 text

import * as _ from 'lodash/has' ^ ERROR: Unexpected token: name ?? expected: punc ?? 55

Slide 57

Slide 57 text

UglifyJS 56

Slide 58

Slide 58 text

Terser github.com/terser-js/terser 57

Slide 59

Slide 59 text

Что можно еще улучшить? 58

Slide 60

Slide 60 text

Treeshaking 59

Slide 61

Slide 61 text

60

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

Мечта function cube(x) { return x * x * x; } console.log(cube(x)) 63

Slide 65

Slide 65 text

Реальность !function(e){var r={};function t(n){if(r[n])return r[n].exports;var o=r[n]= {i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=r,t.d=function(e,r,n) {t.o(e,r)||Object.defineProperty(e,r, {configurable:!1,enumerable:!0,get:n})},t.r=function(e) {Object.defineProperty(e,"__esModule",{value:!0})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=0)}([function(e,r,t){"use strict";t.r(r);var n;console.log((n=3)*n*n)}]); 64

Slide 66

Slide 66 text

Dry противоречит Treeshaking 65

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

Иногда Treeshaking не работал в Webpack 68

Slide 70

Slide 70 text

Статья на Хабре bit.do/treesh 69

Slide 71

Slide 71 text

... /* unused harmony export square */ /* harmony export (immutable) */ __webpa function square(x) { return x * x; } function cube(x) { t * * 70

Slide 72

Slide 72 text

... /* unused harmony export square */ /* harmony export (immutable) */ __webpa function square(x) { return x * x; } function cube(x) { t * * 71

Slide 73

Slide 73 text

module.js export class MyClass { print(){ console.log('find me'); } } 72

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

UglifyJS не понимает, что выкидывать 75

Slide 77

Slide 77 text

... /* unused harmony export MyClass */ var MyClass = /*#__PURE__*/ function ... 76

Slide 78

Slide 78 text

Как починить Babel 7 Webpack 77

Slide 79

Slide 79 text

Как нужно 78

Slide 80

Slide 80 text

Код на Dart void main() { print(cube(3)); } int cube(int x) => x * x * x; int square(int x) => x * x; 79

Slide 81

Slide 81 text

Вывод ... main: function() { H.printString("27"); } ... 80

Slide 82

Slide 82 text

class Car { Rudder rudder; List wheelList; } 81

Slide 83

Slide 83 text

class Rudder { void turn(){ print('turn'); } } class Wheel { void pump(){ print('pump'); } } 82

Slide 84

Slide 84 text

void main(){ final car = new Car(); car.wheelList = [ new Wheel() new Wheel(), new Wheel(), new Wheel(), ]; car.rudder = new Rudder(); car.rudder.turn(); } 83

Slide 85

Slide 85 text

... main: function() { H.printString("turn"); } ... 84

Slide 86

Slide 86 text

— Почему так не может Terser?! 85

Slide 87

Slide 87 text

— Потому что он использует AST 86

Slide 88

Slide 88 text

Dart использует SSA форму для оптимизаций 87

Slide 89

Slide 89 text

SSA (англ. Static single assignment form) — промежуточное представление, используемое компиляторами, в котором каждой переменной значение присваивается лишь единожды 88

Slide 90

Slide 90 text

Книга по SSA bit.do/ssabook 89

Slide 91

Slide 91 text

— Я не хочу писать на Dart! — Есть же Closure Compiler! 90

Slide 92

Slide 92 text

const car = { wheelList: [ new Wheel(), new Wheel(), new Wheel(), new Wheel(), ], rudder: new Rudder(), }; 91

Slide 93

Slide 93 text

console.log("turn"); 92

Slide 94

Slide 94 text

ОПТИМИЗАЦИИ ОПТИМИЗАЦИИ normalize optimizeArgumentsArray aggressiveInlineAliases collapseProperties earlyInlineVariables earlyPeepholeOptimizations removeUnusedCode disambiguateProperties codeRemovingLoop devirtualizePrototypeMethods flowSensitiveInlineVariables mainOptimizationLoop flowSensitiveInlineVariables removeUnusedCode collapseAnonymousFunctions extractPrototypeMemberDeclarations ambiguateProperties renameProperties convertToDottedProperties coalesceVariableNames peepholeOptimizations exploitAssign collapseVariableDeclarations denormalize renameVars renameLabels latePeepholeOptimizations 93

Slide 95

Slide 95 text

94

Slide 96

Slide 96 text

95

Slide 97

Slide 97 text

Сложности 96

Slide 98

Slide 98 text

527k ⟹ 140k java -jar compiler.jar \ --compilation_level ADVANCED \ --language_in=ES6 \ --js lodash.js > out.js 97

Slide 99

Slide 99 text

Нужны типы 98

Slide 100

Slide 100 text

/** * This annotation spans multiple lines * @type {{ * id:number, * val:string, * }} */ 99

Slide 101

Slide 101 text

TSICKLE TSICKLE TypeScript ⟹ Closure Translator 100

Slide 102

Slide 102 text

Если все скомпилировалось в это, то console.log("turn"); 101

Slide 103

Slide 103 text

Как будет работать здесь? var rudder = new Rudder(); 102

Slide 104

Slide 104 text

const car = {}; car.idNumber = '1234'; function displayId(car) { alert(car['idNumber']); } alert(car.idNumber); displayId(car); 103

Slide 105

Slide 105 text

var a={}; a.a="1234"; alert(a.a); alert(a.idNumber); 104

Slide 106

Slide 106 text

prepack.io 105

Slide 107

Slide 107 text

(function () { function fibonacci(x) { return x <= 1 ? x : fibonacci(x - 1 } global.x = fibonacci(15); })(); 106

Slide 108

Slide 108 text

x = 610; 107

Slide 109

Slide 109 text

Lodash? 527k ⇒ 868k 108

Slide 110

Slide 110 text

Выводы Потоковое сжатие Можно оставить как есть GCC не для библиотек Искать другие варианты 109

Slide 111

Slide 111 text

Алексей Золотых twitter: Спасибо за внимание! @zolotyh 110